diff --git a/app/hosts/[hostname]/components/ScreenTimeTab.tsx b/app/hosts/[hostname]/components/ScreenTimeTab.tsx index 2d851fb..317aaf8 100644 --- a/app/hosts/[hostname]/components/ScreenTimeTab.tsx +++ b/app/hosts/[hostname]/components/ScreenTimeTab.tsx @@ -141,9 +141,6 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) {

{periodLabel}

-

- 切换日期时会自动取消上一条请求,图表与排行保持同步刷新。 -

@@ -296,6 +293,10 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) { {app.percentage}%
+
+ 路径: {app.processPath || '路径未知'} +
+
} @@ -111,12 +112,14 @@ const getLocalDateUtcMs = (parts: DateParts, tzOffsetMinutes: number) => { const normalizeProcess = (processPath: string, title: string) => { const rawProcessName = win32.basename(processPath || '').trim() + const rawProcessPath = processPath.trim() const normalizedTitle = cleanTitle(title) const fallbackId = normalizedTitle ? `title:${normalizedTitle.toLowerCase()}` : 'unknown' return { processId: rawProcessName ? rawProcessName.toLowerCase() : fallbackId, - rawProcessName + rawProcessName, + rawProcessPath } } @@ -228,6 +231,7 @@ const addDurationToUsageMap = ( usageMap: Map, processId: string, rawProcessName: string, + rawProcessPath: string, title: string, durationMs: number ) => { @@ -240,12 +244,16 @@ const addDurationToUsageMap = ( if (!existing.rawProcessName && rawProcessName) { existing.rawProcessName = rawProcessName } + if (!existing.rawProcessPath && rawProcessPath) { + existing.rawProcessPath = rawProcessPath + } return } usageMap.set(processId, { processId, rawProcessName, + rawProcessPath, durationMs, titleDurations: new Map([[title, durationMs]]) }) @@ -266,6 +274,7 @@ const buildAppList = (usageMap: Map) => { processId: app.processId, processName: resolveProcessDisplayName(app.rawProcessName, sortedTitles), rawProcessName: app.rawProcessName, + processPath: app.rawProcessPath, durationSeconds: Math.round(app.durationMs / 1000), percentage: totalDurationMs > 0 ? Number(((app.durationMs / totalDurationMs) * 100).toFixed(2)) : 0, titleCount: sortedTitles.length, @@ -384,6 +393,7 @@ async function handleScreenTime(req: NextRequest) { aggregateUsageMap, process.processId, process.rawProcessName, + process.rawProcessPath, title, aggregateContributionMs ) @@ -393,6 +403,7 @@ async function handleScreenTime(req: NextRequest) { selectedDayUsageMap, process.processId, process.rawProcessName, + process.rawProcessPath, title, selectedDayContributionMs ) diff --git a/app/hosts/[hostname]/screenshots/route.ts b/app/hosts/[hostname]/screenshots/route.ts index 16fac23..c18e18b 100644 --- a/app/hosts/[hostname]/screenshots/route.ts +++ b/app/hosts/[hostname]/screenshots/route.ts @@ -3,6 +3,7 @@ import { prisma } from '@/lib/prisma' import { storeFile } from '@/lib/fileStorage' import { push } from '@/lib/push' import { withCors } from '@/lib/middleware' +import { filterIgnoredWindows } from '@/lib/windowFilters' import ffmpeg from 'fluent-ffmpeg' import { writeFileSync, unlinkSync, mkdtempSync, readFileSync, rmSync } from 'fs' import { tmpdir } from 'os' @@ -28,7 +29,8 @@ async function handleScreenshotUpload(req: NextRequest) { const formData = await req.formData() const files: File[] = [] - const windowsInfo: WindowInfo[] = JSON.parse(formData.get('windows_info') as string || '[]') + const rawWindowsInfo: WindowInfo[] = JSON.parse(formData.get('windows_info') as string || '[]') + const windowsInfo = filterIgnoredWindows(rawWindowsInfo) // Extract files from formData for (const [key, value] of formData.entries()) { diff --git a/app/hosts/[hostname]/types.ts b/app/hosts/[hostname]/types.ts index 03cf231..86780fb 100644 --- a/app/hosts/[hostname]/types.ts +++ b/app/hosts/[hostname]/types.ts @@ -33,6 +33,7 @@ export interface ScreenTimeAppUsage { processId: string; processName: string; rawProcessName: string; + processPath: string; durationSeconds: number; percentage: number; titleCount: number; diff --git a/envtests/cleanup-nvidia-overlay.ts b/envtests/cleanup-nvidia-overlay.ts new file mode 100644 index 0000000..e51e9ce --- /dev/null +++ b/envtests/cleanup-nvidia-overlay.ts @@ -0,0 +1,66 @@ +#!/usr/bin/env bun + +import { prisma } from '../lib/prisma' +import { IGNORED_WINDOW_KEYWORDS } from '../lib/windowFilters' + +async function cleanupIgnoredWindows() { + console.log(`开始清理数据库中的忽略窗口记录: ${IGNORED_WINDOW_KEYWORDS.join(', ')}`) + + const whereClause = { + OR: [ + ...IGNORED_WINDOW_KEYWORDS.flatMap(keyword => ([ + { + title: { + contains: keyword, + mode: 'insensitive' as const + } + }, + { + path: { + contains: keyword, + mode: 'insensitive' as const + } + } + ])) + ] + } + + const [matchedCount, affectedHosts] = await Promise.all([ + prisma.window.count({ where: whereClause }), + prisma.window.findMany({ + where: whereClause, + select: { + record: { + select: { + hostname: true + } + } + }, + distinct: ['recordId'] + }) + ]) + + if (matchedCount === 0) { + console.log('没有找到需要清理的忽略窗口记录。') + return + } + + const hostnames = Array.from(new Set(affectedHosts.map(item => item.record.hostname))).sort() + console.log(`匹配到 ${matchedCount} 条窗口记录,涉及 ${hostnames.length} 台主机。`) + console.log(`主机列表: ${hostnames.join(', ')}`) + + const deleteResult = await prisma.window.deleteMany({ + where: whereClause + }) + + console.log(`已删除 ${deleteResult.count} 条忽略窗口记录。`) +} + +cleanupIgnoredWindows() + .catch((error) => { + console.error('清理忽略窗口失败:', error) + process.exitCode = 1 + }) + .finally(async () => { + await prisma.$disconnect() + }) \ No newline at end of file diff --git a/lib/windowFilters.ts b/lib/windowFilters.ts new file mode 100644 index 0000000..c0533c5 --- /dev/null +++ b/lib/windowFilters.ts @@ -0,0 +1,20 @@ +export const IGNORED_WINDOW_KEYWORDS = [ + 'NVIDIA Overlay', + 'Twinkle Tray Panel' +] as const + +const normalizedIgnoredWindowKeywords = IGNORED_WINDOW_KEYWORDS.map(keyword => keyword.toLowerCase()) + +interface WindowLike { + title?: string | null + path?: string | null +} + +export const isIgnoredWindow = ({ title, path }: WindowLike) => { + const normalizedValue = `${title ?? ''}\n${path ?? ''}`.toLowerCase() + return normalizedIgnoredWindowKeywords.some(keyword => normalizedValue.includes(keyword)) +} + +export const filterIgnoredWindows = (windows: T[]) => { + return windows.filter(window => !isIgnoredWindow(window)) +} \ No newline at end of file diff --git a/package.json b/package.json index b265531..c357b66 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "db:generate": "prisma generate", "db:migrate": "prisma migrate dev", "db:reset": "prisma migrate reset", - "db:studio": "prisma studio" + "db:studio": "prisma studio", + "db:cleanup:ignored-windows": "bun envtests/cleanup-nvidia-overlay.ts", + "db:cleanup:nvidia-overlay": "bun envtests/cleanup-nvidia-overlay.ts" }, "dependencies": { "@prisma/client": "^6.10.1",