Files
MajoorWaldi-ComfyUI-Majoor-…/js/components/sidebar/sections/FileInfoSection.js
T
Ewald b6931d21a6 feat(grid): enhance keyboard navigation and selection handling
- Implemented functions to retrieve all assets and grid columns dynamically.
- Added keyboard navigation support for arrow keys and Home/End keys with Shift for range selection.
- Improved asset selection logic and scrolling to selected asset.
- Integrated selection state management with grid container.
- Updated rating shortcuts to be conditionally active based on hotkey state.

feat(panel): enhance Assets Manager and sidebar functionality

- Introduced summary bar disposal mechanism for better resource management.
- Added sidebar refresh logic to update active asset details.
- Improved settings change handling to selectively refresh grid and sidebar.
- Enhanced grid reload mechanism to prevent concurrent reloads.

feat(viewer): add FPS detection for video assets

- Implemented functions to parse and emit detected FPS from asset metadata.
- Attached FPS detection to video elements during metadata loading and runtime.

style(theme): update theme variables and styles

- Added new CSS variables for card hover, selection, rating, and tag colors.
- Enhanced asset card styles for selection and hover states.
- Improved badge colors for better visibility and consistency.

fix(status): improve status indicator feedback

- Added status highlight function to provide visual feedback based on status tone.
- Updated status dot and section styles to reflect current status accurately.

refactor(context): streamline context controller actions

- Added badge CSS variable application to keep UI consistent with live settings.
2026-02-09 14:49:30 +01:00

209 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createParametersBox } from "../utils/dom.js";
import { formatDate, formatTime, formatDuration } from "../../../utils/format.js";
/**
* Create a section displaying file information:
* - Date & Time (creation/modification)
* - Duration (for videos)
* - Generation Time (if available)
* - Dimensions
* - File size
*/
export function createFileInfoSection(asset) {
if (!asset) return null;
const fileData = [];
// Dimensions
if (asset.width && asset.height) {
fileData.push({
label: "Dimensions",
value: `${asset.width} × ${asset.height}`,
tooltip: "Image/video resolution in pixels"
});
}
// Duration (for videos)
if (asset.duration && asset.duration > 0) {
fileData.push({
label: "Duration",
value: formatDuration(asset.duration),
tooltip: "Video duration"
});
}
// FPS + Length (frames) for animated media (video, gif, webp, webm)
if (isAnimatedMedia(asset)) {
const fps = readFps(asset);
if (fps != null) {
fileData.push({
label: "FPS",
value: formatFps(fps),
tooltip: "Native frame rate",
});
}
const frameCount = readFrameCount(asset, fps);
if (frameCount != null) {
fileData.push({
label: "Length",
value: `${Math.max(0, Math.floor(frameCount))} frames`,
tooltip: "Total frame count",
});
}
}
// Generation Time (workflow execution time)
const genTimeMs = asset.generation_time_ms ?? asset.metadata?.generation_time_ms ?? 0;
if (genTimeMs && Number.isFinite(Number(genTimeMs)) && genTimeMs > 0 && genTimeMs < 86400000) {
const secs = (Number(genTimeMs) / 1000).toFixed(1);
// Color based on generation time
let color = "#4CAF50"; // Green for < 10s
if (secs >= 60) color = "#FF9800"; // Orange
else if (secs >= 30) color = "#FFC107"; // Yellow
else if (secs >= 10) color = "#8BC34A"; // Light green
fileData.push({
label: "Generation Time",
value: `${secs}s`,
tooltip: "Time taken to generate this asset (workflow execution time)",
valueStyle: `color: ${color}; font-weight: 600;`
});
}
// Date & Time
const timestamp = asset.generation_time || asset.file_creation_time || asset.mtime || asset.created_at;
if (timestamp) {
const dateStr = formatDate(timestamp);
const timeStr = formatTime(timestamp);
if (dateStr) {
fileData.push({
label: "Date",
value: dateStr,
tooltip: "File creation/generation date"
});
}
if (timeStr) {
fileData.push({
label: "Time",
value: timeStr,
tooltip: "File creation/generation time"
});
}
}
// File size
if (asset.size && asset.size > 0) {
const sizeStr = formatFileSize(asset.size);
fileData.push({
label: "File Size",
value: sizeStr,
tooltip: "File size on disk"
});
}
if (fileData.length === 0) return null;
return createParametersBox("File Info", fileData, "#607D8B", { emphasis: true });
}
/**
* Format file size in human-readable format
*/
function formatFileSize(bytes) {
if (!bytes || bytes <= 0) return "0 B";
const units = ["B", "KB", "MB", "GB"];
let unitIndex = 0;
let size = bytes;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(unitIndex > 0 ? 1 : 0)} ${units[unitIndex]}`;
}
function isAnimatedMedia(asset) {
try {
const kind = String(asset?.kind || "").toLowerCase();
if (kind === "video") return true;
const name = String(asset?.filename || asset?.filepath || asset?.path || "").toLowerCase();
return /\.(gif|webp|webm)$/.test(name);
} catch {
return false;
}
}
function parseFpsValue(value) {
try {
const n = Number(value);
if (Number.isFinite(n) && n > 0) return n;
const s = String(value || "").trim();
if (!s) return null;
if (s.includes("/")) {
const [a, b] = s.split("/");
const na = Number(a);
const nb = Number(b);
if (Number.isFinite(na) && Number.isFinite(nb) && nb !== 0) {
const fps = na / nb;
return Number.isFinite(fps) && fps > 0 ? fps : null;
}
}
const f = Number.parseFloat(s);
return Number.isFinite(f) && f > 0 ? f : null;
} catch {
return null;
}
}
function readFps(asset) {
try {
const raw = asset?.metadata_raw || {};
const ff = raw?.raw_ffprobe || {};
const vs = ff?.video_stream || {};
return (
parseFpsValue(asset?.fps) ??
parseFpsValue(raw?.fps) ??
parseFpsValue(raw?.frame_rate) ??
parseFpsValue(vs?.avg_frame_rate) ??
parseFpsValue(vs?.r_frame_rate)
);
} catch {
return null;
}
}
function readFrameCount(asset, fps) {
try {
const raw = asset?.metadata_raw || {};
const ff = raw?.raw_ffprobe || {};
const vs = ff?.video_stream || {};
const direct =
Number(asset?.frame_count) ||
Number(raw?.frame_count) ||
Number(raw?.frames) ||
Number(vs?.nb_frames) ||
Number(vs?.nb_read_frames) ||
0;
if (Number.isFinite(direct) && direct > 0) return Math.floor(direct);
const dur = Number(asset?.duration ?? raw?.duration ?? vs?.duration);
if (Number.isFinite(dur) && dur > 0 && Number.isFinite(fps) && fps > 0) {
return Math.max(1, Math.round(dur * fps));
}
} catch {}
return null;
}
function formatFps(fps) {
const n = Number(fps);
if (!Number.isFinite(n) || n <= 0) return "";
if (Math.abs(n - Math.round(n)) < 0.001) return `${Math.round(n)} fps`;
return `${n.toFixed(3).replace(/\.?0+$/, "")} fps`;
}