Skip to main content

Recording Model

The Recording model stores metadata about automation session recordings, linking video captures to flows and devices for debugging and analysis.

Schema Definition​

model Recording {
id String @id @default(uuid())
recordingId String @unique
fileName String
filePath String
deviceId String
status String
startTime DateTime
endTime DateTime?
duration Int?
fileSize BigInt?
flowId String?
flowName String?
actionTimestamps Json[] @default([])
bitRate String?
resolution String?
orientation String?
error String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
flow Flow? @relation(fields: [flowId], references: [id])

@@map("recordings")
}

Field Descriptions​

Primary Identification​

FieldTypeDescription
idStringUUID primary key
recordingIdStringUnique recording identifier

File Information​

FieldTypeDescription
fileNameStringRecording filename
filePathStringFull path to recording file
fileSizeBigInt?File size in bytes

Recording Metadata​

FieldTypeDescription
deviceIdStringDevice identifier where recording was made
statusStringRecording status (recording, completed, failed, processing)
startTimeDateTimeWhen recording started
endTimeDateTime?When recording ended
durationInt?Recording duration in milliseconds

Flow Association​

FieldTypeDescription
flowIdString?Associated flow identifier
flowNameString?Cached flow name for display

Technical Details​

FieldTypeDescription
actionTimestampsJson[]Array of action timestamps during recording
bitRateString?Video bitrate (e.g., "8M", "4M")
resolutionString?Video resolution (e.g., "1080x2400", "720p")
orientationString?Screen orientation (portrait, landscape)

Error Handling​

FieldTypeDescription
errorString?Error message if recording failed

Metadata​

FieldTypeDescription
createdAtDateTimeRecording creation timestamp
updatedAtDateTimeLast update timestamp

Relationships​

Belongs To​

  • flow: Flow? - The flow this recording is associated with

Usage Examples​

Create Recording Entry​

const recording = await prisma.recording.create({
data: {
recordingId: "rec-2024-01-01-12-00-00",
fileName: "flow-login-2024-01-01-12-00-00.mp4",
filePath: "/recordings/flow-login-2024-01-01-12-00-00.mp4",
deviceId: "emulator-5554",
status: "recording",
startTime: new Date(),
flowId: "flow-123",
flowName: "Login Flow",
bitRate: "8M",
resolution: "1080x2400"
}
});

Update Recording on Completion​

await prisma.recording.update({
where: { recordingId: "rec-2024-01-01-12-00-00" },
data: {
status: "completed",
endTime: new Date(),
duration: 45000, // 45 seconds
fileSize: 12500000 // 12.5 MB
}
});

Record Action Timestamps​

await prisma.recording.update({
where: { recordingId: "rec-2024-01-01-12-00-00" },
data: {
actionTimestamps: {
push: {
action: "tap",
element: "login_button",
timestamp: Date.now(),
coordinates: { x: 540, y: 1200 }
}
}
}
});

Get Recordings for Flow​

const flowRecordings = await prisma.recording.findMany({
where: {
flowId: "flow-123",
status: "completed"
},
orderBy: { startTime: "desc" },
take: 10
});

Get Failed Recordings​

const failedRecordings = await prisma.recording.findMany({
where: {
status: "failed",
endTime: {
gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours
}
},
select: {
recordingId: true,
flowName: true,
deviceId: true,
error: true,
startTime: true
}
});

Action Timestamps Structure​

Action Event Format​

{
"action": "tap",
"element": "login_button",
"timestamp": 1704120600000,
"coordinates": {
"x": 540,
"y": 1200
},
"confidence": 0.95
}

Complex Action Events​

[
{
"action": "type",
"element": "username_field",
"timestamp": 1704120600000,
"text": "testuser",
"coordinates": { "x": 300, "y": 800 }
},
{
"action": "wait",
"duration": 2000,
"timestamp": 1704120602000
},
{
"action": "swipe",
"startCoordinates": { "x": 540, "y": 2000 },
"endCoordinates": { "x": 540, "y": 1000 },
"duration": 300,
"timestamp": 1704120605000
}
]

Recording Lifecycle​

1. Initialization​

const recording = await createRecording({
flowId: "flow-123",
deviceId: "emulator-5554",
options: {
bitRate: "8M",
resolution: "1080p"
}
});

2. Active Recording​

// Record actions as they happen
await recordAction(recording.id, {
action: "tap",
element: "button",
coordinates: { x: 100, y: 200 }
});

3. Completion​

await completeRecording(recording.id, {
duration: calculateDuration(),
fileSize: await getFileSize()
});

4. Post-Processing​

// Generate thumbnails, compress, etc.
await postProcessRecording(recording.id);

Storage Management​

File Organization​

recordings/
├── flow-{flowId}-{timestamp}.mp4
├── thumbnails/
│ └── recording-{id}-{timestamp}.jpg
└── temp/
└── recording-active-{sessionId}.mp4

Storage Cleanup​

async function cleanupOldRecordings(retentionDays: number = 30) {
const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000);

const oldRecordings = await prisma.recording.findMany({
where: {
createdAt: { lt: cutoffDate },
status: "completed"
},
select: { id: true, filePath: true }
});

// Delete files
for (const recording of oldRecordings) {
try {
await fs.unlink(recording.filePath);
// Delete thumbnails, etc.
} catch (error) {
console.error(`Failed to delete ${recording.filePath}:`, error);
}
}

// Delete database records
await prisma.recording.deleteMany({
where: {
id: { in: oldRecordings.map(r => r.id) }
}
});
}

Performance Considerations​

Indexing​

  • Unique index on recordingId
  • Index on flowId for flow filtering
  • Index on deviceId for device filtering
  • Index on status for status queries
  • Index on startTime for time-based queries
  • Index on createdAt for cleanup operations

Query Optimization​

  • Use select to limit returned fields
  • Implement pagination for recording lists
  • Use streaming for large action timestamp arrays
  • Cache frequently accessed recording metadata

File Storage​

  • Use appropriate storage backend (local, S3, etc.)
  • Implement file compression for large recordings
  • Generate thumbnails for quick browsing
  • Use CDN for distribution if needed

Analytics and Reporting​

Recording Statistics​

async function getRecordingStats(timeRange?: { start: Date; end: Date }) {
const whereClause = timeRange ? {
startTime: {
gte: timeRange.start,
lte: timeRange.end
}
} : {};

const stats = await prisma.recording.aggregate({
where: whereClause,
_count: true,
_sum: {
duration: true,
fileSize: true
}
});

const statusBreakdown = await prisma.recording.groupBy({
by: ['status'],
where: whereClause,
_count: true
});

return {
totalRecordings: stats._count,
totalDuration: stats._sum.duration,
totalSize: stats._sum.fileSize,
averageDuration: stats._sum.duration / stats._count,
statusBreakdown: statusBreakdown.reduce((acc, stat) => {
acc[stat.status] = stat._count;
return acc;
}, {})
};
}

Flow Performance Analysis​

async function analyzeFlowPerformance(flowId: string) {
const recordings = await prisma.recording.findMany({
where: {
flowId,
status: "completed"
},
select: {
duration: true,
actionTimestamps: true,
startTime: true
}
});

// Analyze performance patterns
const avgDuration = recordings.reduce((sum, r) => sum + (r.duration || 0), 0) / recordings.length;

const actionCounts = recordings.flatMap(r => r.actionTimestamps).length / recordings.length;

return {
averageDuration: avgDuration,
averageActions: actionCounts,
recordingCount: recordings.length
};
}

Best Practices​

File Management​

  • Implement automatic cleanup policies
  • Use descriptive filenames with timestamps
  • Compress recordings for storage efficiency
  • Generate metadata for quick access

Error Handling​

  • Log recording failures with details
  • Implement retry logic for transient failures
  • Provide fallback recording options
  • Monitor recording success rates

Security​

  • Secure access to recording files
  • Implement user-based access controls
  • Encrypt sensitive recording data
  • Regular security audits of stored files

Monitoring​

  • Track recording success/failure rates
  • Monitor storage usage and costs
  • Alert on recording failures
  • Analyze performance trends

Integration​

  • Sync recordings with external systems
  • Provide APIs for recording access
  • Implement search and filtering capabilities
  • Support multiple output formats