feat: 添加遮罩筛选

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
feie9454 2026-04-25 13:02:05 +08:00
parent d1c5b818d9
commit d82872ad18
7 changed files with 109 additions and 6 deletions

View File

@ -141,9 +141,6 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) {
<h2 className="mt-2 text-2xl font-semibold text-gray-900 dark:text-white">
{periodLabel}
</h2>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
</p>
</div>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
@ -296,6 +293,10 @@ export default function ScreenTimeTab({ hostname }: ScreenTimeTabProps) {
<span>{app.percentage}%</span>
</div>
<div className="mt-2 truncate text-xs text-gray-500 dark:text-gray-400" title={app.processPath || '路径未知'}>
: {app.processPath || '路径未知'}
</div>
<div className="mt-3 h-2 overflow-hidden rounded-full bg-gray-100 dark:bg-gray-700">
<div
className="h-full rounded-full bg-blue-600 dark:bg-blue-500"

View File

@ -28,6 +28,7 @@ interface TitleUsage {
interface MutableAppUsage {
processId: string
rawProcessName: string
rawProcessPath: string
durationMs: number
titleDurations: Map<string, number>
}
@ -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<string, MutableAppUsage>,
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<string, MutableAppUsage>) => {
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
)

View File

@ -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()) {

View File

@ -33,6 +33,7 @@ export interface ScreenTimeAppUsage {
processId: string;
processName: string;
rawProcessName: string;
processPath: string;
durationSeconds: number;
percentage: number;
titleCount: number;

View File

@ -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()
})

20
lib/windowFilters.ts Normal file
View File

@ -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 = <T extends WindowLike>(windows: T[]) => {
return windows.filter(window => !isIgnoredWindow(window))
}

View File

@ -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",