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