mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
@@ -97,7 +97,7 @@ class VideoService {
|
|||||||
return realMd5
|
return realMd5
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Silently fail
|
// 忽略错误
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,10 +105,21 @@ class VideoService {
|
|||||||
|
|
||||||
// 方法2:使用 wcdbService.execQuery 查询加密的 hardlink.db
|
// 方法2:使用 wcdbService.execQuery 查询加密的 hardlink.db
|
||||||
if (dbPath) {
|
if (dbPath) {
|
||||||
const encryptedDbPaths = [
|
// 检查 dbPath 是否已经包含 wxid
|
||||||
join(dbPath, wxid, 'db_storage', 'hardlink', 'hardlink.db'),
|
const dbPathLower = dbPath.toLowerCase()
|
||||||
join(dbPath, cleanedWxid, 'db_storage', 'hardlink', 'hardlink.db')
|
const wxidLower = wxid.toLowerCase()
|
||||||
]
|
const cleanedWxidLower = cleanedWxid.toLowerCase()
|
||||||
|
const dbPathContainsWxid = dbPathLower.includes(wxidLower) || dbPathLower.includes(cleanedWxidLower)
|
||||||
|
|
||||||
|
const encryptedDbPaths: string[] = []
|
||||||
|
if (dbPathContainsWxid) {
|
||||||
|
// dbPath 已包含 wxid,不需要再拼接
|
||||||
|
encryptedDbPaths.push(join(dbPath, 'db_storage', 'hardlink', 'hardlink.db'))
|
||||||
|
} else {
|
||||||
|
// dbPath 不包含 wxid,需要拼接
|
||||||
|
encryptedDbPaths.push(join(dbPath, wxid, 'db_storage', 'hardlink', 'hardlink.db'))
|
||||||
|
encryptedDbPaths.push(join(dbPath, cleanedWxid, 'db_storage', 'hardlink', 'hardlink.db'))
|
||||||
|
}
|
||||||
|
|
||||||
for (const p of encryptedDbPaths) {
|
for (const p of encryptedDbPaths) {
|
||||||
if (existsSync(p)) {
|
if (existsSync(p)) {
|
||||||
@@ -129,6 +140,7 @@ class VideoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// 忽略错误
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +167,6 @@ class VideoService {
|
|||||||
* 文件命名: {md5}.mp4, {md5}.jpg, {md5}_thumb.jpg
|
* 文件命名: {md5}.mp4, {md5}.jpg, {md5}_thumb.jpg
|
||||||
*/
|
*/
|
||||||
async getVideoInfo(videoMd5: string): Promise<VideoInfo> {
|
async getVideoInfo(videoMd5: string): Promise<VideoInfo> {
|
||||||
|
|
||||||
const dbPath = this.getDbPath()
|
const dbPath = this.getDbPath()
|
||||||
const wxid = this.getMyWxid()
|
const wxid = this.getMyWxid()
|
||||||
|
|
||||||
@@ -166,7 +177,19 @@ class VideoService {
|
|||||||
// 先尝试从数据库查询真正的视频文件名
|
// 先尝试从数据库查询真正的视频文件名
|
||||||
const realVideoMd5 = await this.queryVideoFileName(videoMd5) || videoMd5
|
const realVideoMd5 = await this.queryVideoFileName(videoMd5) || videoMd5
|
||||||
|
|
||||||
const videoBaseDir = join(dbPath, wxid, 'msg', 'video')
|
// 检查 dbPath 是否已经包含 wxid,避免重复拼接
|
||||||
|
const dbPathLower = dbPath.toLowerCase()
|
||||||
|
const wxidLower = wxid.toLowerCase()
|
||||||
|
const cleanedWxid = this.cleanWxid(wxid)
|
||||||
|
|
||||||
|
let videoBaseDir: string
|
||||||
|
if (dbPathLower.includes(wxidLower) || dbPathLower.includes(cleanedWxid.toLowerCase())) {
|
||||||
|
// dbPath 已经包含 wxid,直接使用
|
||||||
|
videoBaseDir = join(dbPath, 'msg', 'video')
|
||||||
|
} else {
|
||||||
|
// dbPath 不包含 wxid,需要拼接
|
||||||
|
videoBaseDir = join(dbPath, wxid, 'msg', 'video')
|
||||||
|
}
|
||||||
|
|
||||||
if (!existsSync(videoBaseDir)) {
|
if (!existsSync(videoBaseDir)) {
|
||||||
return { exists: false }
|
return { exists: false }
|
||||||
@@ -202,7 +225,7 @@ class VideoService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[VideoService] Error searching for video:', e)
|
// 忽略错误
|
||||||
}
|
}
|
||||||
|
|
||||||
return { exists: false }
|
return { exists: false }
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ export class WcdbCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isLogEnabled(): boolean {
|
private isLogEnabled(): boolean {
|
||||||
if (process.env.WEFLOW_WORKER === '1') return false
|
// 移除 Worker 线程的日志禁用逻辑,允许在 Worker 中记录日志
|
||||||
if (process.env.WCDB_LOG_ENABLED === '1') return true
|
if (process.env.WCDB_LOG_ENABLED === '1') return true
|
||||||
return this.logEnabled
|
return this.logEnabled
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2146,8 +2146,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.video-placeholder,
|
.video-placeholder,
|
||||||
.video-loading,
|
.video-loading {
|
||||||
.video-unavailable {
|
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -2167,6 +2166,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-unavailable {
|
||||||
|
min-width: 160px;
|
||||||
|
min-height: 120px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: 12px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.clicked {
|
||||||
|
transform: scale(0.98);
|
||||||
|
box-shadow: 0 0 0 2px var(--primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-action {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-quaternary);
|
||||||
|
}
|
||||||
|
|
||||||
.video-loading {
|
.video-loading {
|
||||||
.spin {
|
.spin {
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
|
|||||||
@@ -2155,6 +2155,9 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat, o
|
|||||||
}, [isVoice, message.localId, requestVoiceTranscript])
|
}, [isVoice, message.localId, requestVoiceTranscript])
|
||||||
|
|
||||||
// 视频懒加载
|
// 视频懒加载
|
||||||
|
const videoAutoLoadTriggered = useRef(false)
|
||||||
|
const [videoClicked, setVideoClicked] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isVideo || !videoContainerRef.current) return
|
if (!isVideo || !videoContainerRef.current) return
|
||||||
|
|
||||||
@@ -2178,19 +2181,18 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat, o
|
|||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [isVideo])
|
}, [isVideo])
|
||||||
|
|
||||||
// 加载视频信息
|
// 视频加载中状态引用,避免依赖问题
|
||||||
useEffect(() => {
|
const videoLoadingRef = useRef(false)
|
||||||
if (!isVideo || !isVideoVisible || videoInfo || videoLoading) return
|
|
||||||
if (!videoMd5) {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 加载视频信息(添加重试机制)
|
||||||
|
const requestVideoInfo = useCallback(async () => {
|
||||||
|
if (!videoMd5 || videoLoadingRef.current) return
|
||||||
|
|
||||||
|
videoLoadingRef.current = true
|
||||||
setVideoLoading(true)
|
setVideoLoading(true)
|
||||||
window.electronAPI.video.getVideoInfo(videoMd5).then((result: { success: boolean; exists: boolean; videoUrl?: string; coverUrl?: string; thumbUrl?: string; error?: string }) => {
|
try {
|
||||||
|
const result = await window.electronAPI.video.getVideoInfo(videoMd5)
|
||||||
if (result && result.success) {
|
if (result && result.success && result.exists) {
|
||||||
setVideoInfo({
|
setVideoInfo({
|
||||||
exists: result.exists,
|
exists: result.exists,
|
||||||
videoUrl: result.videoUrl,
|
videoUrl: result.videoUrl,
|
||||||
@@ -2198,16 +2200,25 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat, o
|
|||||||
thumbUrl: result.thumbUrl
|
thumbUrl: result.thumbUrl
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.error('[Video Debug] Video info failed:', result)
|
|
||||||
setVideoInfo({ exists: false })
|
setVideoInfo({ exists: false })
|
||||||
}
|
}
|
||||||
}).catch((err: unknown) => {
|
} catch (err) {
|
||||||
console.error('[Video Debug] getVideoInfo error:', err)
|
|
||||||
setVideoInfo({ exists: false })
|
setVideoInfo({ exists: false })
|
||||||
}).finally(() => {
|
} finally {
|
||||||
|
videoLoadingRef.current = false
|
||||||
setVideoLoading(false)
|
setVideoLoading(false)
|
||||||
})
|
}
|
||||||
}, [isVideo, isVideoVisible, videoInfo, videoLoading, videoMd5])
|
}, [videoMd5])
|
||||||
|
|
||||||
|
// 视频进入视野时自动加载
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isVideo || !isVideoVisible) return
|
||||||
|
if (videoInfo?.exists) return // 已成功加载,不需要重试
|
||||||
|
if (videoAutoLoadTriggered.current) return
|
||||||
|
|
||||||
|
videoAutoLoadTriggered.current = true
|
||||||
|
void requestVideoInfo()
|
||||||
|
}, [isVideo, isVideoVisible, videoInfo, requestVideoInfo])
|
||||||
|
|
||||||
|
|
||||||
// 根据设置决定是否自动转写
|
// 根据设置决定是否自动转写
|
||||||
@@ -2366,16 +2377,27 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat, o
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 视频不存在
|
// 视频不存在 - 添加点击重试功能
|
||||||
if (!videoInfo?.exists || !videoInfo.videoUrl) {
|
if (!videoInfo?.exists || !videoInfo.videoUrl) {
|
||||||
return (
|
return (
|
||||||
<div className="video-unavailable" ref={videoContainerRef}>
|
<button
|
||||||
|
className={`video-unavailable ${videoClicked ? 'clicked' : ''}`}
|
||||||
|
ref={videoContainerRef}
|
||||||
|
onClick={() => {
|
||||||
|
setVideoClicked(true)
|
||||||
|
setTimeout(() => setVideoClicked(false), 800)
|
||||||
|
videoAutoLoadTriggered.current = false
|
||||||
|
void requestVideoInfo()
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
<polygon points="23 7 16 12 23 17 23 7"></polygon>
|
<polygon points="23 7 16 12 23 17 23 7"></polygon>
|
||||||
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
|
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
|
||||||
</svg>
|
</svg>
|
||||||
<span>视频不可用</span>
|
<span>视频未找到</span>
|
||||||
</div>
|
<span className="video-action">{videoClicked ? '已点击…' : '点击重试'}</span>
|
||||||
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user