Implement job metadata extraction and task management features. Add validation for frame range limits, enhance job and task data structures, and introduce new API endpoints for metadata and task retrieval. Update client-side components to handle metadata extraction and display task statuses. Improve error handling in API responses.
This commit is contained in:
@@ -5,6 +5,7 @@ import VideoPlayer from './VideoPlayer';
|
||||
export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
const [jobDetails, setJobDetails] = useState(job);
|
||||
const [files, setFiles] = useState([]);
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [videoUrl, setVideoUrl] = useState(null);
|
||||
const [selectedTaskId, setSelectedTaskId] = useState(null);
|
||||
@@ -42,12 +43,14 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
|
||||
const loadDetails = async () => {
|
||||
try {
|
||||
const [details, fileList] = await Promise.all([
|
||||
const [details, fileList, taskList] = await Promise.all([
|
||||
jobs.get(job.id),
|
||||
jobs.getFiles(job.id),
|
||||
jobs.getTasks(job.id),
|
||||
]);
|
||||
setJobDetails(details);
|
||||
setFiles(fileList);
|
||||
setTasks(taskList);
|
||||
|
||||
// Check if there's an MP4 output file
|
||||
const mp4File = fileList.find(
|
||||
@@ -151,6 +154,16 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
}
|
||||
};
|
||||
|
||||
const getTaskStatusColor = (status) => {
|
||||
const colors = {
|
||||
pending: 'bg-yellow-100 text-yellow-800',
|
||||
running: 'bg-blue-100 text-blue-800',
|
||||
completed: 'bg-green-100 text-green-800',
|
||||
failed: 'bg-red-100 text-red-800',
|
||||
};
|
||||
return colors[status] || 'bg-gray-100 text-gray-800';
|
||||
};
|
||||
|
||||
const outputFiles = files.filter((f) => f.file_type === 'output');
|
||||
const inputFiles = files.filter((f) => f.file_type === 'input');
|
||||
|
||||
@@ -269,9 +282,42 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">
|
||||
Task Execution
|
||||
Tasks
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{tasks.length > 0 && (
|
||||
<div className="bg-gray-50 rounded-lg p-4 mb-4">
|
||||
<h4 className="font-medium text-gray-900 mb-2">Task List</h4>
|
||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||
{tasks.map((task) => (
|
||||
<div
|
||||
key={task.id}
|
||||
onClick={() => handleTaskClick(task.id)}
|
||||
className={`flex items-center justify-between p-3 bg-white rounded cursor-pointer hover:bg-gray-100 transition-colors ${
|
||||
selectedTaskId === task.id ? 'ring-2 ring-purple-600' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className={`px-2 py-1 rounded text-xs font-medium ${getTaskStatusColor(task.status)}`}>
|
||||
{task.status}
|
||||
</span>
|
||||
<span className="font-medium text-gray-900">
|
||||
Frame {task.frame_start}
|
||||
{task.frame_end !== task.frame_start ? `-${task.frame_end}` : ''}
|
||||
</span>
|
||||
{task.task_type && task.task_type !== 'render' && (
|
||||
<span className="text-xs text-gray-500">({task.task_type})</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
{task.runner_id && `Runner ${task.runner_id}`}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{taskSteps.length > 0 && (
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<h4 className="font-medium text-gray-900 mb-2">Steps</h4>
|
||||
|
||||
Reference in New Issue
Block a user