Implement context archive handling and metadata extraction for render jobs. Add functionality to check for Blender availability, create context archives, and extract metadata from .blend files. Update job creation and retrieval processes to support new metadata structure and context file management. Enhance client-side components to display context files and integrate new API endpoints for context handling.
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { jobs } from '../utils/api';
|
||||
import VideoPlayer from './VideoPlayer';
|
||||
import FileExplorer from './FileExplorer';
|
||||
|
||||
export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
const [jobDetails, setJobDetails] = useState(job);
|
||||
const [files, setFiles] = useState([]);
|
||||
const [contextFiles, setContextFiles] = useState([]);
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [videoUrl, setVideoUrl] = useState(null);
|
||||
@@ -89,6 +91,15 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
setJobDetails(details);
|
||||
setFiles(fileList);
|
||||
setTasks(taskList);
|
||||
|
||||
// Fetch context archive contents separately (may not exist for old jobs)
|
||||
try {
|
||||
const contextList = await jobs.getContextArchive(job.id);
|
||||
setContextFiles(contextList || []);
|
||||
} catch (error) {
|
||||
// Context archive may not exist for old jobs
|
||||
setContextFiles([]);
|
||||
}
|
||||
|
||||
// Only load task data (logs/steps) for tasks that don't have data yet
|
||||
// This prevents overwriting logs that are being streamed via WebSocket
|
||||
@@ -446,7 +457,7 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{videoUrl && jobDetails.output_format === 'MP4' && (
|
||||
{videoUrl && (jobDetails.output_format === 'EXR_264_MP4' || jobDetails.output_format === 'EXR_AV1_MP4') && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-100 mb-3">
|
||||
Video Preview
|
||||
@@ -455,68 +466,38 @@ export default function JobDetails({ job, onClose, onUpdate }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contextFiles.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-100 mb-3">
|
||||
Context Archive
|
||||
</h3>
|
||||
<FileExplorer
|
||||
files={contextFiles.map(f => ({
|
||||
id: 0, // Context files don't have IDs
|
||||
file_name: f.path || f.name || '',
|
||||
file_size: f.size || 0,
|
||||
file_type: 'input'
|
||||
}))}
|
||||
onDownload={null} // Context files can't be downloaded individually
|
||||
isImageFile={isImageFile}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{outputFiles.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-100 mb-3">
|
||||
Output Files
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{outputFiles.map((file) => {
|
||||
const isImage = isImageFile(file.file_name);
|
||||
const imageUrl = isImage ? jobs.downloadFile(job.id, file.id) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={file.id}
|
||||
className="flex items-center justify-between p-3 bg-gray-900 rounded-lg border border-gray-700"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium text-gray-100">{file.file_name}</p>
|
||||
<p className="text-sm text-gray-400">
|
||||
{(file.file_size / 1024 / 1024).toFixed(2)} MB
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{isImage && imageUrl && (
|
||||
<button
|
||||
onClick={() => setPreviewImage({ url: imageUrl, fileName: file.file_name })}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-500 transition-colors"
|
||||
>
|
||||
Preview
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleDownload(file.id, file.file_name)}
|
||||
className="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-500 transition-colors"
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{inputFiles.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-100 mb-3">
|
||||
Input Files
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{inputFiles.map((file) => (
|
||||
<div
|
||||
key={file.id}
|
||||
className="p-3 bg-gray-900 rounded-lg border border-gray-700"
|
||||
>
|
||||
<p className="font-medium text-gray-100">{file.file_name}</p>
|
||||
<p className="text-sm text-gray-400">
|
||||
{(file.file_size / 1024 / 1024).toFixed(2)} MB
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<FileExplorer
|
||||
files={outputFiles}
|
||||
onDownload={handleDownload}
|
||||
onPreview={(file) => {
|
||||
const imageUrl = jobs.downloadFile(job.id, file.id);
|
||||
setPreviewImage({ url: imageUrl, fileName: file.file_name });
|
||||
}}
|
||||
isImageFile={isImageFile}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user