mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-25 07:16:51 +00:00
refactor(export): remove task pause/stop and prioritize export by loaded message counts
This commit is contained in:
@@ -95,11 +95,6 @@ let isDownloadInProgress = false
|
|||||||
let downloadProgressHandler: ((progress: any) => void) | null = null
|
let downloadProgressHandler: ((progress: any) => void) | null = null
|
||||||
let downloadedHandler: (() => void) | null = null
|
let downloadedHandler: (() => void) | null = null
|
||||||
|
|
||||||
interface ExportTaskControlState {
|
|
||||||
pauseRequested: boolean
|
|
||||||
stopRequested: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type AnnualReportYearsLoadStrategy = 'cache' | 'native' | 'hybrid'
|
type AnnualReportYearsLoadStrategy = 'cache' | 'native' | 'hybrid'
|
||||||
type AnnualReportYearsLoadPhase = 'cache' | 'native' | 'scan' | 'done'
|
type AnnualReportYearsLoadPhase = 'cache' | 'native' | 'scan' | 'done'
|
||||||
|
|
||||||
@@ -126,31 +121,11 @@ interface AnnualReportYearsTaskState {
|
|||||||
updatedAt: number
|
updatedAt: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportTaskControlMap = new Map<string, ExportTaskControlState>()
|
|
||||||
const pendingExportTaskControlMap = new Map<string, ExportTaskControlState>()
|
|
||||||
const annualReportYearsLoadTasks = new Map<string, AnnualReportYearsTaskState>()
|
const annualReportYearsLoadTasks = new Map<string, AnnualReportYearsTaskState>()
|
||||||
const annualReportYearsTaskByCacheKey = new Map<string, string>()
|
const annualReportYearsTaskByCacheKey = new Map<string, string>()
|
||||||
const annualReportYearsSnapshotCache = new Map<string, { snapshot: AnnualReportYearsProgressPayload; updatedAt: number; taskId: string }>()
|
const annualReportYearsSnapshotCache = new Map<string, { snapshot: AnnualReportYearsProgressPayload; updatedAt: number; taskId: string }>()
|
||||||
const annualReportYearsSnapshotTtlMs = 10 * 60 * 1000
|
const annualReportYearsSnapshotTtlMs = 10 * 60 * 1000
|
||||||
|
|
||||||
const getTaskControlState = (taskId?: string): ExportTaskControlState | null => {
|
|
||||||
const normalized = typeof taskId === 'string' ? taskId.trim() : ''
|
|
||||||
if (!normalized) return null
|
|
||||||
return exportTaskControlMap.get(normalized) || null
|
|
||||||
}
|
|
||||||
|
|
||||||
const createTaskControlState = (taskId?: string): string | null => {
|
|
||||||
const normalized = typeof taskId === 'string' ? taskId.trim() : ''
|
|
||||||
if (!normalized) return null
|
|
||||||
const pending = pendingExportTaskControlMap.get(normalized)
|
|
||||||
exportTaskControlMap.set(normalized, {
|
|
||||||
pauseRequested: Boolean(pending?.pauseRequested),
|
|
||||||
stopRequested: Boolean(pending?.stopRequested)
|
|
||||||
})
|
|
||||||
pendingExportTaskControlMap.delete(normalized)
|
|
||||||
return normalized
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizeAnnualReportYearsSnapshot = (snapshot: AnnualReportYearsProgressPayload): AnnualReportYearsProgressPayload => {
|
const normalizeAnnualReportYearsSnapshot = (snapshot: AnnualReportYearsProgressPayload): AnnualReportYearsProgressPayload => {
|
||||||
const years = Array.isArray(snapshot.years) ? [...snapshot.years] : []
|
const years = Array.isArray(snapshot.years) ? [...snapshot.years] : []
|
||||||
return { ...snapshot, years }
|
return { ...snapshot, years }
|
||||||
@@ -212,22 +187,6 @@ const isYearsLoadCanceled = (taskId: string): boolean => {
|
|||||||
return task?.canceled === true
|
return task?.canceled === true
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearTaskControlState = (taskId?: string): void => {
|
|
||||||
const normalized = typeof taskId === 'string' ? taskId.trim() : ''
|
|
||||||
if (!normalized) return
|
|
||||||
exportTaskControlMap.delete(normalized)
|
|
||||||
pendingExportTaskControlMap.delete(normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
const queueTaskControlRequest = (taskId: string, action: 'pause' | 'stop'): void => {
|
|
||||||
const normalized = taskId.trim()
|
|
||||||
if (!normalized) return
|
|
||||||
const existing = pendingExportTaskControlMap.get(normalized) || { pauseRequested: false, stopRequested: false }
|
|
||||||
if (action === 'pause') existing.pauseRequested = true
|
|
||||||
if (action === 'stop') existing.stopRequested = true
|
|
||||||
pendingExportTaskControlMap.set(normalized, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWindow(options: { autoShow?: boolean } = {}) {
|
function createWindow(options: { autoShow?: boolean } = {}) {
|
||||||
// 获取图标路径 - 打包后在 resources 目录
|
// 获取图标路径 - 打包后在 resources 目录
|
||||||
const { autoShow = true } = options
|
const { autoShow = true } = options
|
||||||
@@ -1269,27 +1228,17 @@ function registerIpcHandlers() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('sns:exportTimeline', async (event, options: any) => {
|
ipcMain.handle('sns:exportTimeline', async (event, options: any) => {
|
||||||
const taskId = typeof options?.taskId === 'string' ? options.taskId : undefined
|
|
||||||
const controlId = createTaskControlState(taskId)
|
|
||||||
const exportOptions = { ...(options || {}) }
|
const exportOptions = { ...(options || {}) }
|
||||||
delete exportOptions.taskId
|
delete exportOptions.taskId
|
||||||
|
|
||||||
try {
|
return snsService.exportTimeline(
|
||||||
return snsService.exportTimeline(
|
exportOptions,
|
||||||
exportOptions,
|
(progress) => {
|
||||||
(progress) => {
|
if (!event.sender.isDestroyed()) {
|
||||||
if (!event.sender.isDestroyed()) {
|
event.sender.send('sns:exportProgress', progress)
|
||||||
event.sender.send('sns:exportProgress', progress)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
shouldPause: () => Boolean(getTaskControlState(controlId || undefined)?.pauseRequested),
|
|
||||||
shouldStop: () => Boolean(getTaskControlState(controlId || undefined)?.stopRequested)
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
} finally {
|
)
|
||||||
clearTaskControlState(controlId || undefined)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('sns:selectExportDir', async () => {
|
ipcMain.handle('sns:selectExportDir', async () => {
|
||||||
@@ -1412,42 +1361,14 @@ function registerIpcHandlers() {
|
|||||||
return exportService.getExportStats(sessionIds, options)
|
return exportService.getExportStats(sessionIds, options)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('export:exportSessions', async (event, sessionIds: string[], outputDir: string, options: ExportOptions, taskId?: string) => {
|
ipcMain.handle('export:exportSessions', async (event, sessionIds: string[], outputDir: string, options: ExportOptions) => {
|
||||||
const controlId = createTaskControlState(taskId)
|
|
||||||
const onProgress = (progress: ExportProgress) => {
|
const onProgress = (progress: ExportProgress) => {
|
||||||
if (!event.sender.isDestroyed()) {
|
if (!event.sender.isDestroyed()) {
|
||||||
event.sender.send('export:progress', progress)
|
event.sender.send('export:progress', progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return exportService.exportSessions(sessionIds, outputDir, options, onProgress)
|
||||||
return exportService.exportSessions(sessionIds, outputDir, options, onProgress, {
|
|
||||||
shouldPause: () => Boolean(getTaskControlState(controlId || undefined)?.pauseRequested),
|
|
||||||
shouldStop: () => Boolean(getTaskControlState(controlId || undefined)?.stopRequested)
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
clearTaskControlState(controlId || undefined)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.handle('export:pauseTask', async (_, taskId: string) => {
|
|
||||||
const state = getTaskControlState(taskId)
|
|
||||||
if (!state) {
|
|
||||||
queueTaskControlRequest(taskId, 'pause')
|
|
||||||
return { success: true, queued: true }
|
|
||||||
}
|
|
||||||
state.pauseRequested = true
|
|
||||||
return { success: true }
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.handle('export:stopTask', async (_, taskId: string) => {
|
|
||||||
const state = getTaskControlState(taskId)
|
|
||||||
if (!state) {
|
|
||||||
queueTaskControlRequest(taskId, 'stop')
|
|
||||||
return { success: true, queued: true }
|
|
||||||
}
|
|
||||||
state.stopRequested = true
|
|
||||||
return { success: true }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('export:exportSession', async (_, sessionId: string, outputPath: string, options: ExportOptions) => {
|
ipcMain.handle('export:exportSession', async (_, sessionId: string, outputPath: string, options: ExportOptions) => {
|
||||||
|
|||||||
@@ -306,16 +306,12 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
export: {
|
export: {
|
||||||
getExportStats: (sessionIds: string[], options: any) =>
|
getExportStats: (sessionIds: string[], options: any) =>
|
||||||
ipcRenderer.invoke('export:getExportStats', sessionIds, options),
|
ipcRenderer.invoke('export:getExportStats', sessionIds, options),
|
||||||
exportSessions: (sessionIds: string[], outputDir: string, options: any, taskId?: string) =>
|
exportSessions: (sessionIds: string[], outputDir: string, options: any) =>
|
||||||
ipcRenderer.invoke('export:exportSessions', sessionIds, outputDir, options, taskId),
|
ipcRenderer.invoke('export:exportSessions', sessionIds, outputDir, options),
|
||||||
exportSession: (sessionId: string, outputPath: string, options: any) =>
|
exportSession: (sessionId: string, outputPath: string, options: any) =>
|
||||||
ipcRenderer.invoke('export:exportSession', sessionId, outputPath, options),
|
ipcRenderer.invoke('export:exportSession', sessionId, outputPath, options),
|
||||||
exportContacts: (outputDir: string, options: any) =>
|
exportContacts: (outputDir: string, options: any) =>
|
||||||
ipcRenderer.invoke('export:exportContacts', outputDir, options),
|
ipcRenderer.invoke('export:exportContacts', outputDir, options),
|
||||||
pauseTask: (taskId: string) =>
|
|
||||||
ipcRenderer.invoke('export:pauseTask', taskId),
|
|
||||||
stopTask: (taskId: string) =>
|
|
||||||
ipcRenderer.invoke('export:stopTask', taskId),
|
|
||||||
onProgress: (callback: (payload: { current: number; total: number; currentSession: string; currentSessionId?: string; phase: string }) => void) => {
|
onProgress: (callback: (payload: { current: number; total: number; currentSession: string; currentSessionId?: string; phase: string }) => void) => {
|
||||||
ipcRenderer.on('export:progress', (_, payload) => callback(payload))
|
ipcRenderer.on('export:progress', (_, payload) => callback(payload))
|
||||||
return () => ipcRenderer.removeAllListeners('export:progress')
|
return () => ipcRenderer.removeAllListeners('export:progress')
|
||||||
|
|||||||
@@ -6331,15 +6331,15 @@ class ExportService {
|
|||||||
total: sessionIds.length,
|
total: sessionIds.length,
|
||||||
currentSession: sessionInfo.displayName,
|
currentSession: sessionInfo.displayName,
|
||||||
currentSessionId: sessionId,
|
currentSessionId: sessionId,
|
||||||
phase: 'exporting',
|
phase: 'complete',
|
||||||
phaseLabel: '该会话没有消息,已跳过'
|
phaseLabel: '该会话没有消息,已跳过'
|
||||||
})
|
})
|
||||||
return 'done'
|
return 'done'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emptySessionIds.has(sessionId)) {
|
if (emptySessionIds.has(sessionId)) {
|
||||||
failCount++
|
successCount++
|
||||||
failedSessionIds.push(sessionId)
|
successSessionIds.push(sessionId)
|
||||||
activeSessionRatios.delete(sessionId)
|
activeSessionRatios.delete(sessionId)
|
||||||
completedCount++
|
completedCount++
|
||||||
onProgress?.({
|
onProgress?.({
|
||||||
@@ -6347,7 +6347,7 @@ class ExportService {
|
|||||||
total: sessionIds.length,
|
total: sessionIds.length,
|
||||||
currentSession: sessionInfo.displayName,
|
currentSession: sessionInfo.displayName,
|
||||||
currentSessionId: sessionId,
|
currentSessionId: sessionId,
|
||||||
phase: 'exporting',
|
phase: 'complete',
|
||||||
phaseLabel: '该会话没有消息,已跳过'
|
phaseLabel: '该会话没有消息,已跳过'
|
||||||
})
|
})
|
||||||
return 'done'
|
return 'done'
|
||||||
@@ -6419,7 +6419,7 @@ class ExportService {
|
|||||||
total: sessionIds.length,
|
total: sessionIds.length,
|
||||||
currentSession: sessionInfo.displayName,
|
currentSession: sessionInfo.displayName,
|
||||||
currentSessionId: sessionId,
|
currentSessionId: sessionId,
|
||||||
phase: 'exporting',
|
phase: 'complete',
|
||||||
phaseLabel: '无变化,已跳过'
|
phaseLabel: '无变化,已跳过'
|
||||||
})
|
})
|
||||||
return 'done'
|
return 'done'
|
||||||
@@ -6463,6 +6463,14 @@ class ExportService {
|
|||||||
failCount++
|
failCount++
|
||||||
failedSessionIds.push(sessionId)
|
failedSessionIds.push(sessionId)
|
||||||
console.error(`导出 ${sessionId} 失败:`, result.error)
|
console.error(`导出 ${sessionId} 失败:`, result.error)
|
||||||
|
onProgress?.({
|
||||||
|
current: computeAggregateCurrent(),
|
||||||
|
total: sessionIds.length,
|
||||||
|
currentSession: sessionInfo.displayName,
|
||||||
|
currentSessionId: sessionId,
|
||||||
|
phase: 'complete',
|
||||||
|
phaseLabel: '导出失败'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
activeSessionRatios.delete(sessionId)
|
activeSessionRatios.delete(sessionId)
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ import { useContactTypeCountsStore } from '../stores/contactTypeCountsStore'
|
|||||||
import './ExportPage.scss'
|
import './ExportPage.scss'
|
||||||
|
|
||||||
type ConversationTab = 'private' | 'group' | 'official' | 'former_friend'
|
type ConversationTab = 'private' | 'group' | 'official' | 'former_friend'
|
||||||
type TaskStatus = 'queued' | 'running' | 'paused' | 'stopped' | 'success' | 'error'
|
type TaskStatus = 'queued' | 'running' | 'success' | 'error'
|
||||||
type TaskControlState = 'pausing' | 'stopping'
|
|
||||||
type TaskScope = 'single' | 'multi' | 'content' | 'sns'
|
type TaskScope = 'single' | 'multi' | 'content' | 'sns'
|
||||||
type ContentType = 'text' | 'voice' | 'image' | 'video' | 'emoji'
|
type ContentType = 'text' | 'voice' | 'image' | 'video' | 'emoji'
|
||||||
type ContentCardType = ContentType | 'sns'
|
type ContentCardType = ContentType | 'sns'
|
||||||
@@ -124,7 +123,6 @@ interface ExportTask {
|
|||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
status: TaskStatus
|
status: TaskStatus
|
||||||
controlState?: TaskControlState
|
|
||||||
createdAt: number
|
createdAt: number
|
||||||
startedAt?: number
|
startedAt?: number
|
||||||
finishedAt?: number
|
finishedAt?: number
|
||||||
@@ -353,13 +351,7 @@ const formatDurationMs = (ms: number): string => {
|
|||||||
|
|
||||||
const getTaskStatusLabel = (task: ExportTask): string => {
|
const getTaskStatusLabel = (task: ExportTask): string => {
|
||||||
if (task.status === 'queued') return '排队中'
|
if (task.status === 'queued') return '排队中'
|
||||||
if (task.status === 'running') {
|
if (task.status === 'running') return '进行中'
|
||||||
if (task.controlState === 'pausing') return '暂停中'
|
|
||||||
if (task.controlState === 'stopping') return '停止中'
|
|
||||||
return '进行中'
|
|
||||||
}
|
|
||||||
if (task.status === 'paused') return '已暂停'
|
|
||||||
if (task.status === 'stopped') return '已停止'
|
|
||||||
if (task.status === 'success') return '已完成'
|
if (task.status === 'success') return '已完成'
|
||||||
return '失败'
|
return '失败'
|
||||||
}
|
}
|
||||||
@@ -429,6 +421,7 @@ const parseDateInput = (value: string, endOfDay: boolean): Date => {
|
|||||||
|
|
||||||
const toKindByContactType = (session: AppChatSession, contact?: ContactInfo): ConversationTab => {
|
const toKindByContactType = (session: AppChatSession, contact?: ContactInfo): ConversationTab => {
|
||||||
if (session.username.endsWith('@chatroom')) return 'group'
|
if (session.username.endsWith('@chatroom')) return 'group'
|
||||||
|
if (session.username.startsWith('gh_')) return 'official'
|
||||||
if (contact?.type === 'official') return 'official'
|
if (contact?.type === 'official') return 'official'
|
||||||
if (contact?.type === 'former_friend') return 'former_friend'
|
if (contact?.type === 'former_friend') return 'former_friend'
|
||||||
return 'private'
|
return 'private'
|
||||||
@@ -445,6 +438,13 @@ const isContentScopeSession = (session: SessionRow): boolean => (
|
|||||||
session.kind === 'private' || session.kind === 'group' || session.kind === 'former_friend'
|
session.kind === 'private' || session.kind === 'group' || session.kind === 'former_friend'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const exportKindPriority: Record<ConversationTab, number> = {
|
||||||
|
private: 0,
|
||||||
|
group: 1,
|
||||||
|
former_friend: 2,
|
||||||
|
official: 3
|
||||||
|
}
|
||||||
|
|
||||||
const getAvatarLetter = (name: string): string => {
|
const getAvatarLetter = (name: string): string => {
|
||||||
if (!name) return '?'
|
if (!name) return '?'
|
||||||
return [...name][0] || '?'
|
return [...name][0] || '?'
|
||||||
@@ -2277,7 +2277,6 @@ function ExportPage() {
|
|||||||
updateTask(next.id, task => ({
|
updateTask(next.id, task => ({
|
||||||
...task,
|
...task,
|
||||||
status: 'running',
|
status: 'running',
|
||||||
controlState: undefined,
|
|
||||||
startedAt: Date.now(),
|
startedAt: Date.now(),
|
||||||
finishedAt: undefined,
|
finishedAt: undefined,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
@@ -2338,43 +2337,17 @@ function ExportPage() {
|
|||||||
exportLivePhotos: snsOptions.exportLivePhotos,
|
exportLivePhotos: snsOptions.exportLivePhotos,
|
||||||
exportVideos: snsOptions.exportVideos,
|
exportVideos: snsOptions.exportVideos,
|
||||||
startTime: snsOptions.startTime,
|
startTime: snsOptions.startTime,
|
||||||
endTime: snsOptions.endTime,
|
endTime: snsOptions.endTime
|
||||||
taskId: next.id
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
updateTask(next.id, task => ({
|
updateTask(next.id, task => ({
|
||||||
...task,
|
...task,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: Date.now(),
|
finishedAt: Date.now(),
|
||||||
error: result.error || '朋友圈导出失败',
|
error: result.error || '朋友圈导出失败',
|
||||||
performance: finalizeTaskPerformance(task, Date.now())
|
performance: finalizeTaskPerformance(task, Date.now())
|
||||||
}))
|
}))
|
||||||
} else if (result.stopped) {
|
|
||||||
updateTask(next.id, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'stopped',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: Date.now(),
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
phaseLabel: '已停止'
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, Date.now())
|
|
||||||
}))
|
|
||||||
} else if (result.paused) {
|
|
||||||
updateTask(next.id, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'paused',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: Date.now(),
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
phaseLabel: '已暂停'
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, Date.now())
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
const doneAt = Date.now()
|
const doneAt = Date.now()
|
||||||
const exportedPosts = Math.max(0, result.postCount || 0)
|
const exportedPosts = Math.max(0, result.postCount || 0)
|
||||||
@@ -2386,7 +2359,6 @@ function ExportPage() {
|
|||||||
updateTask(next.id, task => ({
|
updateTask(next.id, task => ({
|
||||||
...task,
|
...task,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
finishedAt: doneAt,
|
||||||
progress: {
|
progress: {
|
||||||
...task.progress,
|
...task.progress,
|
||||||
@@ -2407,23 +2379,19 @@ function ExportPage() {
|
|||||||
const result = await window.electronAPI.export.exportSessions(
|
const result = await window.electronAPI.export.exportSessions(
|
||||||
next.payload.sessionIds,
|
next.payload.sessionIds,
|
||||||
next.payload.outputDir,
|
next.payload.outputDir,
|
||||||
next.payload.options,
|
next.payload.options
|
||||||
next.id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
updateTask(next.id, task => ({
|
updateTask(next.id, task => ({
|
||||||
...task,
|
...task,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: Date.now(),
|
finishedAt: Date.now(),
|
||||||
error: result.error || '导出失败',
|
error: result.error || '导出失败',
|
||||||
performance: finalizeTaskPerformance(task, Date.now())
|
performance: finalizeTaskPerformance(task, Date.now())
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
const doneAt = Date.now()
|
const doneAt = Date.now()
|
||||||
const successCount = result.successCount ?? 0
|
|
||||||
const failCount = result.failCount ?? 0
|
|
||||||
const contentTypes = next.payload.contentType
|
const contentTypes = next.payload.contentType
|
||||||
? [next.payload.contentType]
|
? [next.payload.contentType]
|
||||||
: inferContentTypesFromOptions(next.payload.options)
|
: inferContentTypesFromOptions(next.payload.options)
|
||||||
@@ -2435,83 +2403,20 @@ function ExportPage() {
|
|||||||
markContentExported(successSessionIds, contentTypes, doneAt)
|
markContentExported(successSessionIds, contentTypes, doneAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.stopped) {
|
updateTask(next.id, task => ({
|
||||||
updateTask(next.id, task => ({
|
...task,
|
||||||
...task,
|
status: 'success',
|
||||||
status: 'stopped',
|
finishedAt: doneAt,
|
||||||
controlState: undefined,
|
progress: {
|
||||||
finishedAt: doneAt,
|
...task.progress,
|
||||||
progress: {
|
current: task.progress.total || next.payload.sessionIds.length,
|
||||||
...task.progress,
|
total: task.progress.total || next.payload.sessionIds.length,
|
||||||
current: successCount + failCount,
|
phaseLabel: '完成',
|
||||||
total: task.progress.total || next.payload.sessionIds.length,
|
phaseProgress: 1,
|
||||||
phaseLabel: '已停止'
|
phaseTotal: 1
|
||||||
},
|
},
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
performance: finalizeTaskPerformance(task, doneAt)
|
||||||
}))
|
}))
|
||||||
} else if (result.paused) {
|
|
||||||
const pendingSessionIds = Array.isArray(result.pendingSessionIds)
|
|
||||||
? result.pendingSessionIds
|
|
||||||
: []
|
|
||||||
const sessionNameMap = new Map<string, string>()
|
|
||||||
next.payload.sessionIds.forEach((sessionId, index) => {
|
|
||||||
sessionNameMap.set(sessionId, next.payload.sessionNames[index] || sessionId)
|
|
||||||
})
|
|
||||||
const pendingSessionNames = pendingSessionIds.map(sessionId => sessionNameMap.get(sessionId) || sessionId)
|
|
||||||
|
|
||||||
if (pendingSessionIds.length === 0) {
|
|
||||||
updateTask(next.id, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'success',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
current: task.progress.total || next.payload.sessionIds.length,
|
|
||||||
total: task.progress.total || next.payload.sessionIds.length,
|
|
||||||
phaseLabel: '完成',
|
|
||||||
phaseProgress: 1,
|
|
||||||
phaseTotal: 1
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
updateTask(next.id, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'paused',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
|
||||||
payload: {
|
|
||||||
...task.payload,
|
|
||||||
sessionIds: pendingSessionIds,
|
|
||||||
sessionNames: pendingSessionNames
|
|
||||||
},
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
current: successCount + failCount,
|
|
||||||
total: task.progress.total || next.payload.sessionIds.length,
|
|
||||||
phaseLabel: '已暂停'
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateTask(next.id, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'success',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
current: task.progress.total || next.payload.sessionIds.length,
|
|
||||||
total: task.progress.total || next.payload.sessionIds.length,
|
|
||||||
phaseLabel: '完成',
|
|
||||||
phaseProgress: 1,
|
|
||||||
phaseTotal: 1
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2519,7 +2424,6 @@ function ExportPage() {
|
|||||||
updateTask(next.id, task => ({
|
updateTask(next.id, task => ({
|
||||||
...task,
|
...task,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
finishedAt: doneAt,
|
||||||
error: String(error),
|
error: String(error),
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
performance: finalizeTaskPerformance(task, doneAt)
|
||||||
@@ -2543,91 +2447,6 @@ function ExportPage() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const pauseTask = useCallback(async (taskId: string) => {
|
|
||||||
const target = tasksRef.current.find(task => task.id === taskId)
|
|
||||||
if (!target) return
|
|
||||||
|
|
||||||
if (target.status === 'queued') {
|
|
||||||
updateTask(taskId, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'paused',
|
|
||||||
controlState: undefined,
|
|
||||||
performance: finalizeTaskPerformance(task, Date.now())
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.status !== 'running') return
|
|
||||||
|
|
||||||
updateTask(taskId, task => (
|
|
||||||
task.status === 'running'
|
|
||||||
? { ...task, controlState: 'pausing' }
|
|
||||||
: task
|
|
||||||
))
|
|
||||||
|
|
||||||
const result = await window.electronAPI.export.pauseTask(taskId)
|
|
||||||
if (!result.success) {
|
|
||||||
updateTask(taskId, task => (
|
|
||||||
task.status === 'running'
|
|
||||||
? { ...task, controlState: undefined }
|
|
||||||
: task
|
|
||||||
))
|
|
||||||
window.alert(result.error || '暂停任务失败,请重试')
|
|
||||||
}
|
|
||||||
}, [updateTask])
|
|
||||||
|
|
||||||
const resumeTask = useCallback((taskId: string) => {
|
|
||||||
updateTask(taskId, task => {
|
|
||||||
if (task.status !== 'paused') return task
|
|
||||||
return {
|
|
||||||
...task,
|
|
||||||
status: 'queued',
|
|
||||||
controlState: undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [updateTask])
|
|
||||||
|
|
||||||
const stopTask = useCallback(async (taskId: string) => {
|
|
||||||
const target = tasksRef.current.find(task => task.id === taskId)
|
|
||||||
if (!target) return
|
|
||||||
const shouldStop = window.confirm('确认停止该导出任务吗?')
|
|
||||||
if (!shouldStop) return
|
|
||||||
|
|
||||||
if (target.status === 'queued' || target.status === 'paused') {
|
|
||||||
const doneAt = Date.now()
|
|
||||||
updateTask(taskId, task => ({
|
|
||||||
...task,
|
|
||||||
status: 'stopped',
|
|
||||||
controlState: undefined,
|
|
||||||
finishedAt: doneAt,
|
|
||||||
progress: {
|
|
||||||
...task.progress,
|
|
||||||
phaseLabel: '已停止'
|
|
||||||
},
|
|
||||||
performance: finalizeTaskPerformance(task, doneAt)
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.status !== 'running') return
|
|
||||||
|
|
||||||
updateTask(taskId, task => (
|
|
||||||
task.status === 'running'
|
|
||||||
? { ...task, controlState: 'stopping' }
|
|
||||||
: task
|
|
||||||
))
|
|
||||||
|
|
||||||
const result = await window.electronAPI.export.stopTask(taskId)
|
|
||||||
if (!result.success) {
|
|
||||||
updateTask(taskId, task => (
|
|
||||||
task.status === 'running'
|
|
||||||
? { ...task, controlState: undefined }
|
|
||||||
: task
|
|
||||||
))
|
|
||||||
window.alert(result.error || '停止任务失败,请重试')
|
|
||||||
}
|
|
||||||
}, [updateTask])
|
|
||||||
|
|
||||||
const createTask = async () => {
|
const createTask = async () => {
|
||||||
if (!exportDialog.open || !exportFolder) return
|
if (!exportDialog.open || !exportFolder) return
|
||||||
if (exportDialog.scope !== 'sns' && exportDialog.sessionIds.length === 0) return
|
if (exportDialog.scope !== 'sns' && exportDialog.sessionIds.length === 0) return
|
||||||
@@ -2688,12 +2507,45 @@ function ExportPage() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resolveSessionExistingMessageCount = useCallback((session: SessionRow): number => {
|
||||||
|
const counted = normalizeMessageCount(sessionMessageCounts[session.username])
|
||||||
|
if (typeof counted === 'number') return counted
|
||||||
|
const hinted = normalizeMessageCount(session.messageCountHint)
|
||||||
|
if (typeof hinted === 'number') return hinted
|
||||||
|
return 0
|
||||||
|
}, [sessionMessageCounts])
|
||||||
|
|
||||||
|
const orderSessionsForExport = useCallback((source: SessionRow[]): SessionRow[] => {
|
||||||
|
return source
|
||||||
|
.filter((session) => session.hasSession && isContentScopeSession(session))
|
||||||
|
.map((session) => ({
|
||||||
|
session,
|
||||||
|
count: resolveSessionExistingMessageCount(session)
|
||||||
|
}))
|
||||||
|
.filter((item) => item.count > 0)
|
||||||
|
.sort((a, b) => {
|
||||||
|
const kindDiff = exportKindPriority[a.session.kind] - exportKindPriority[b.session.kind]
|
||||||
|
if (kindDiff !== 0) return kindDiff
|
||||||
|
if (a.count !== b.count) return b.count - a.count
|
||||||
|
const tsA = a.session.sortTimestamp || a.session.lastTimestamp || 0
|
||||||
|
const tsB = b.session.sortTimestamp || b.session.lastTimestamp || 0
|
||||||
|
if (tsA !== tsB) return tsB - tsA
|
||||||
|
return (a.session.displayName || a.session.username)
|
||||||
|
.localeCompare(b.session.displayName || b.session.username, 'zh-Hans-CN')
|
||||||
|
})
|
||||||
|
.map((item) => item.session)
|
||||||
|
}, [resolveSessionExistingMessageCount])
|
||||||
|
|
||||||
const openBatchExport = () => {
|
const openBatchExport = () => {
|
||||||
const selectable = new Set(sessions.filter(session => session.hasSession).map(session => session.username))
|
const selectedSet = new Set(selectedSessions)
|
||||||
const ids = Array.from(selectedSessions).filter(id => selectable.has(id))
|
const selectedRows = sessions.filter((session) => selectedSet.has(session.username))
|
||||||
if (ids.length === 0) return
|
const orderedRows = orderSessionsForExport(selectedRows)
|
||||||
const nameMap = new Map(sessions.map(session => [session.username, session.displayName || session.username]))
|
if (orderedRows.length === 0) {
|
||||||
const names = ids.map(id => nameMap.get(id) || id)
|
window.alert('所选会话暂无可导出的消息(总消息数为 0)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const ids = orderedRows.map((session) => session.username)
|
||||||
|
const names = orderedRows.map((session) => session.displayName || session.username)
|
||||||
|
|
||||||
openExportDialog({
|
openExportDialog({
|
||||||
scope: 'multi',
|
scope: 'multi',
|
||||||
@@ -2704,13 +2556,13 @@ function ExportPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openContentExport = (contentType: ContentType) => {
|
const openContentExport = (contentType: ContentType) => {
|
||||||
const ids = sessions
|
const orderedRows = orderSessionsForExport(sessions)
|
||||||
.filter(session => session.hasSession && isContentScopeSession(session))
|
if (orderedRows.length === 0) {
|
||||||
.map(session => session.username)
|
window.alert('当前会话列表暂无可导出的消息(总消息数为 0)')
|
||||||
|
return
|
||||||
const names = sessions
|
}
|
||||||
.filter(session => session.hasSession && isContentScopeSession(session))
|
const ids = orderedRows.map((session) => session.username)
|
||||||
.map(session => session.displayName || session.username)
|
const names = orderedRows.map((session) => session.displayName || session.username)
|
||||||
|
|
||||||
openExportDialog({
|
openExportDialog({
|
||||||
scope: 'content',
|
scope: 'content',
|
||||||
@@ -2752,17 +2604,6 @@ function ExportPage() {
|
|||||||
return set
|
return set
|
||||||
}, [tasks])
|
}, [tasks])
|
||||||
|
|
||||||
const pausedSessionIds = useMemo(() => {
|
|
||||||
const set = new Set<string>()
|
|
||||||
for (const task of tasks) {
|
|
||||||
if (task.status !== 'paused') continue
|
|
||||||
for (const id of task.payload.sessionIds) {
|
|
||||||
set.add(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return set
|
|
||||||
}, [tasks])
|
|
||||||
|
|
||||||
const inProgressSessionIds = useMemo(() => {
|
const inProgressSessionIds = useMemo(() => {
|
||||||
const set = new Set<string>()
|
const set = new Set<string>()
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
@@ -3567,7 +3408,6 @@ function ExportPage() {
|
|||||||
|
|
||||||
const isRunning = runningSessionIds.has(session.username)
|
const isRunning = runningSessionIds.has(session.username)
|
||||||
const isQueued = queuedSessionIds.has(session.username)
|
const isQueued = queuedSessionIds.has(session.username)
|
||||||
const isPaused = pausedSessionIds.has(session.username)
|
|
||||||
const recent = formatRecentExportTime(lastExportBySession[session.username], nowTick)
|
const recent = formatRecentExportTime(lastExportBySession[session.username], nowTick)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -3580,8 +3420,8 @@ function ExportPage() {
|
|||||||
详情
|
详情
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`row-export-btn ${isRunning ? 'running' : ''} ${isPaused ? 'paused' : ''}`}
|
className={`row-export-btn ${isRunning ? 'running' : ''}`}
|
||||||
disabled={isRunning || isPaused}
|
disabled={isRunning}
|
||||||
onClick={() => openSingleExport(session)}
|
onClick={() => openSingleExport(session)}
|
||||||
>
|
>
|
||||||
{isRunning ? (
|
{isRunning ? (
|
||||||
@@ -3589,7 +3429,7 @@ function ExportPage() {
|
|||||||
<Loader2 size={14} className="spin" />
|
<Loader2 size={14} className="spin" />
|
||||||
导出中
|
导出中
|
||||||
</>
|
</>
|
||||||
) : isPaused ? '已暂停' : isQueued ? '排队中' : '导出'}
|
) : isQueued ? '排队中' : '导出'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{recent && <span className="row-export-time">{recent}</span>}
|
{recent && <span className="row-export-time">{recent}</span>}
|
||||||
@@ -3668,7 +3508,6 @@ function ExportPage() {
|
|||||||
const isSnsCardStatsLoading = !hasSeededSnsStats
|
const isSnsCardStatsLoading = !hasSeededSnsStats
|
||||||
const taskRunningCount = tasks.filter(task => task.status === 'running').length
|
const taskRunningCount = tasks.filter(task => task.status === 'running').length
|
||||||
const taskQueuedCount = tasks.filter(task => task.status === 'queued').length
|
const taskQueuedCount = tasks.filter(task => task.status === 'queued').length
|
||||||
const taskPausedCount = tasks.filter(task => task.status === 'paused').length
|
|
||||||
const showInitialSkeleton = isLoading && sessions.length === 0
|
const showInitialSkeleton = isLoading && sessions.length === 0
|
||||||
const chooseExportFolder = useCallback(async () => {
|
const chooseExportFolder = useCallback(async () => {
|
||||||
const result = await window.electronAPI.dialog.openFile({
|
const result = await window.electronAPI.dialog.openFile({
|
||||||
@@ -3722,7 +3561,6 @@ function ExportPage() {
|
|||||||
<div className="task-summary">
|
<div className="task-summary">
|
||||||
<span>进行中 {taskRunningCount}</span>
|
<span>进行中 {taskRunningCount}</span>
|
||||||
<span>排队 {taskQueuedCount}</span>
|
<span>排队 {taskQueuedCount}</span>
|
||||||
<span>暂停 {taskPausedCount}</span>
|
|
||||||
<span>总计 {tasks.length}</span>
|
<span>总计 {tasks.length}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -3756,7 +3594,7 @@ function ExportPage() {
|
|||||||
<div className="task-center-modal-header">
|
<div className="task-center-modal-header">
|
||||||
<div className="task-center-modal-title">
|
<div className="task-center-modal-title">
|
||||||
<h3>任务中心</h3>
|
<h3>任务中心</h3>
|
||||||
<span>进行中 {taskRunningCount} · 排队 {taskQueuedCount} · 暂停 {taskPausedCount} · 总计 {tasks.length}</span>
|
<span>进行中 {taskRunningCount} · 排队 {taskQueuedCount} · 总计 {tasks.length}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="close-icon-btn"
|
className="close-icon-btn"
|
||||||
@@ -3795,14 +3633,14 @@ function ExportPage() {
|
|||||||
? Math.max(0, Math.min(1, task.progress.phaseProgress / task.progress.phaseTotal))
|
? Math.max(0, Math.min(1, task.progress.phaseProgress / task.progress.phaseTotal))
|
||||||
: null
|
: null
|
||||||
return (
|
return (
|
||||||
<div key={task.id} className={`task-card ${task.status} ${task.controlState ? `request-${task.controlState}` : ''}`}>
|
<div key={task.id} className={`task-card ${task.status}`}>
|
||||||
<div className="task-main">
|
<div className="task-main">
|
||||||
<div className="task-title">{task.title}</div>
|
<div className="task-title">{task.title}</div>
|
||||||
<div className="task-meta">
|
<div className="task-meta">
|
||||||
<span className={`task-status ${task.status}`}>{getTaskStatusLabel(task)}</span>
|
<span className={`task-status ${task.status}`}>{getTaskStatusLabel(task)}</span>
|
||||||
<span>{new Date(task.createdAt).toLocaleString('zh-CN')}</span>
|
<span>{new Date(task.createdAt).toLocaleString('zh-CN')}</span>
|
||||||
</div>
|
</div>
|
||||||
{(task.status === 'running' || task.status === 'paused') && (
|
{task.status === 'running' && (
|
||||||
<>
|
<>
|
||||||
<div className="task-progress-bar">
|
<div className="task-progress-bar">
|
||||||
<div
|
<div
|
||||||
@@ -3882,35 +3720,6 @@ function ExportPage() {
|
|||||||
{isPerfExpanded ? '收起详情' : '性能详情'}
|
{isPerfExpanded ? '收起详情' : '性能详情'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{(task.status === 'running' || task.status === 'queued') && (
|
|
||||||
<button
|
|
||||||
className="task-action-btn"
|
|
||||||
type="button"
|
|
||||||
onClick={() => void pauseTask(task.id)}
|
|
||||||
disabled={task.status === 'running' && task.controlState === 'pausing'}
|
|
||||||
>
|
|
||||||
{task.status === 'running' && task.controlState === 'pausing' ? '暂停中' : '暂停'}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{task.status === 'paused' && (
|
|
||||||
<button
|
|
||||||
className="task-action-btn primary"
|
|
||||||
type="button"
|
|
||||||
onClick={() => resumeTask(task.id)}
|
|
||||||
>
|
|
||||||
继续
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{(task.status === 'running' || task.status === 'queued' || task.status === 'paused') && (
|
|
||||||
<button
|
|
||||||
className="task-action-btn danger"
|
|
||||||
type="button"
|
|
||||||
onClick={() => void stopTask(task.id)}
|
|
||||||
disabled={task.status === 'running' && task.controlState === 'stopping'}
|
|
||||||
>
|
|
||||||
{task.status === 'running' && task.controlState === 'stopping' ? '停止中' : '停止'}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button className="task-action-btn" onClick={() => task.payload.outputDir && void window.electronAPI.shell.openPath(task.payload.outputDir)}>
|
<button className="task-action-btn" onClick={() => task.payload.outputDir && void window.electronAPI.shell.openPath(task.payload.outputDir)}>
|
||||||
<FolderOpen size={14} /> 目录
|
<FolderOpen size={14} /> 目录
|
||||||
</button>
|
</button>
|
||||||
@@ -4253,7 +4062,6 @@ function ExportPage() {
|
|||||||
const canExport = Boolean(matchedSession?.hasSession)
|
const canExport = Boolean(matchedSession?.hasSession)
|
||||||
const isRunning = canExport && runningSessionIds.has(contact.username)
|
const isRunning = canExport && runningSessionIds.has(contact.username)
|
||||||
const isQueued = canExport && queuedSessionIds.has(contact.username)
|
const isQueued = canExport && queuedSessionIds.has(contact.username)
|
||||||
const isPaused = canExport && pausedSessionIds.has(contact.username)
|
|
||||||
const recent = canExport ? formatRecentExportTime(lastExportBySession[contact.username], nowTick) : ''
|
const recent = canExport ? formatRecentExportTime(lastExportBySession[contact.username], nowTick) : ''
|
||||||
const countedMessages = normalizeMessageCount(sessionMessageCounts[contact.username])
|
const countedMessages = normalizeMessageCount(sessionMessageCounts[contact.username])
|
||||||
const hintedMessages = normalizeMessageCount(matchedSession?.messageCountHint)
|
const hintedMessages = normalizeMessageCount(matchedSession?.messageCountHint)
|
||||||
@@ -4300,8 +4108,8 @@ function ExportPage() {
|
|||||||
详情
|
详情
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`row-export-btn ${isRunning ? 'running' : ''} ${isPaused ? 'paused' : ''} ${!canExport ? 'no-session' : ''}`}
|
className={`row-export-btn ${isRunning ? 'running' : ''} ${!canExport ? 'no-session' : ''}`}
|
||||||
disabled={!canExport || isRunning || isPaused}
|
disabled={!canExport || isRunning}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!matchedSession || !matchedSession.hasSession) return
|
if (!matchedSession || !matchedSession.hasSession) return
|
||||||
openSingleExport({
|
openSingleExport({
|
||||||
@@ -4315,7 +4123,7 @@ function ExportPage() {
|
|||||||
<Loader2 size={14} className="spin" />
|
<Loader2 size={14} className="spin" />
|
||||||
导出中
|
导出中
|
||||||
</>
|
</>
|
||||||
) : !canExport ? '暂无会话' : isPaused ? '已暂停' : isQueued ? '排队中' : '导出'}
|
) : !canExport ? '暂无会话' : isQueued ? '排队中' : '导出'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{recent && <span className="row-export-time">{recent}</span>}
|
{recent && <span className="row-export-time">{recent}</span>}
|
||||||
|
|||||||
9
src/types/electron.d.ts
vendored
9
src/types/electron.d.ts
vendored
@@ -692,12 +692,10 @@ export interface ElectronAPI {
|
|||||||
estimatedSeconds: number
|
estimatedSeconds: number
|
||||||
sessions: Array<{ sessionId: string; displayName: string; totalCount: number; voiceCount: number }>
|
sessions: Array<{ sessionId: string; displayName: string; totalCount: number; voiceCount: number }>
|
||||||
}>
|
}>
|
||||||
exportSessions: (sessionIds: string[], outputDir: string, options: ExportOptions, taskId?: string) => Promise<{
|
exportSessions: (sessionIds: string[], outputDir: string, options: ExportOptions) => Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
successCount?: number
|
successCount?: number
|
||||||
failCount?: number
|
failCount?: number
|
||||||
paused?: boolean
|
|
||||||
stopped?: boolean
|
|
||||||
pendingSessionIds?: string[]
|
pendingSessionIds?: string[]
|
||||||
successSessionIds?: string[]
|
successSessionIds?: string[]
|
||||||
failedSessionIds?: string[]
|
failedSessionIds?: string[]
|
||||||
@@ -712,8 +710,6 @@ export interface ElectronAPI {
|
|||||||
successCount?: number
|
successCount?: number
|
||||||
error?: string
|
error?: string
|
||||||
}>
|
}>
|
||||||
pauseTask: (taskId: string) => Promise<{ success: boolean; error?: string }>
|
|
||||||
stopTask: (taskId: string) => Promise<{ success: boolean; error?: string }>
|
|
||||||
onProgress: (callback: (payload: ExportProgress) => void) => () => void
|
onProgress: (callback: (payload: ExportProgress) => void) => () => void
|
||||||
}
|
}
|
||||||
whisper: {
|
whisper: {
|
||||||
@@ -767,8 +763,7 @@ export interface ElectronAPI {
|
|||||||
exportVideos?: boolean
|
exportVideos?: boolean
|
||||||
startTime?: number
|
startTime?: number
|
||||||
endTime?: number
|
endTime?: number
|
||||||
taskId?: string
|
}) => Promise<{ success: boolean; filePath?: string; postCount?: number; mediaCount?: number; error?: string }>
|
||||||
}) => Promise<{ success: boolean; filePath?: string; postCount?: number; mediaCount?: number; paused?: boolean; stopped?: boolean; error?: string }>
|
|
||||||
onExportProgress: (callback: (payload: { current: number; total: number; status: string }) => void) => () => void
|
onExportProgress: (callback: (payload: { current: number; total: number; status: string }) => void) => () => void
|
||||||
selectExportDir: () => Promise<{ canceled: boolean; filePath?: string }>
|
selectExportDir: () => Promise<{ canceled: boolean; filePath?: string }>
|
||||||
getSnsUsernames: () => Promise<{ success: boolean; usernames?: string[]; error?: string }>
|
getSnsUsernames: () => Promise<{ success: boolean; usernames?: string[]; error?: string }>
|
||||||
|
|||||||
Reference in New Issue
Block a user