Backend (Django 6.1 + DRF): - Token auth with a custom User model (register/login/logout/me) - Library models (MediaItem, MediaLocation) and REST endpoints - File list/detail with search, rating filter, sorting, pagination - Multipart upload with optional rating/tags/notes - Range-aware media serving (video seeking) and ffmpeg thumbnails - scan_files management command for the watched folder Frontend (React 19 + Vite + TypeScript): - Catppuccin Mocha design tokens from the design docs - App shell, token persistence, protected routes - Library grid with filters, file detail with custom data editor - Upload page with per-file progress via XHR - Dev proxy to the Django API
21 lines
572 B
TypeScript
21 lines
572 B
TypeScript
export function formatBytes(bytes: number): string {
|
|
if (!Number.isFinite(bytes) || bytes < 0) return "—";
|
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
let value = bytes;
|
|
let unit = 0;
|
|
while (value >= 1024 && unit < units.length - 1) {
|
|
value /= 1024;
|
|
unit += 1;
|
|
}
|
|
const digits = value >= 100 || unit === 0 ? 0 : 1;
|
|
return `${value.toFixed(digits)} ${units[unit]}`;
|
|
}
|
|
|
|
export function formatDate(value: string): string {
|
|
return new Date(value).toLocaleDateString(undefined, {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric",
|
|
});
|
|
}
|