feat: media & music file name

This commit is contained in:
2026-06-07 21:53:01 +03:30
parent 2be0f3e2b8
commit 944d3bc00a
3 changed files with 24 additions and 4 deletions
+7 -2
View File
@@ -18,7 +18,7 @@ import {
Badge, Badge,
} from "@/components/ui"; } from "@/components/ui";
import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons"; import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons";
import { formatMinutes } from "@/lib/utils"; import { formatMinutes, fileNameToTitle } from "@/lib/utils";
import { MediaPreview } from "@/components/MediaPreview"; import { MediaPreview } from "@/components/MediaPreview";
import { ImagePicker } from "@/components/ImagePicker"; import { ImagePicker } from "@/components/ImagePicker";
import { pickUrl, SOUND_KEYS, VIDEO_KEYS, IMAGE_KEYS } from "@/lib/media"; import { pickUrl, SOUND_KEYS, VIDEO_KEYS, IMAGE_KEYS } from "@/lib/media";
@@ -397,7 +397,12 @@ export default function MediaPage() {
<Input <Input
type="file" type="file"
accept="audio/*,video/*" accept="audio/*,video/*"
onChange={(e) => setFile(e.target.files?.[0] ?? null)} onChange={(e) => {
const f = e.target.files?.[0] ?? null;
setFile(f);
// Fill the title from the file name if still empty.
if (f && !title.trim()) setTitle(fileNameToTitle(f.name));
}}
/> />
</Field> </Field>
+7 -2
View File
@@ -15,7 +15,7 @@ import {
PageHeader, PageHeader,
} from "@/components/ui"; } from "@/components/ui";
import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons"; import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons";
import { formatMinutes } from "@/lib/utils"; import { formatMinutes, fileNameToTitle } from "@/lib/utils";
import { MediaPreview } from "@/components/MediaPreview"; import { MediaPreview } from "@/components/MediaPreview";
import { ImagePicker } from "@/components/ImagePicker"; import { ImagePicker } from "@/components/ImagePicker";
import { pickUrl, SOUND_KEYS, IMAGE_KEYS } from "@/lib/media"; import { pickUrl, SOUND_KEYS, IMAGE_KEYS } from "@/lib/media";
@@ -301,7 +301,12 @@ export default function MusicTracksPage() {
<Input <Input
type="file" type="file"
accept="audio/*" accept="audio/*"
onChange={(e) => setFile(e.target.files?.[0] ?? null)} onChange={(e) => {
const f = e.target.files?.[0] ?? null;
setFile(f);
// Fill the title from the file name if still empty.
if (f && !title.trim()) setTitle(fileNameToTitle(f.name));
}}
required={!editing} required={!editing}
/> />
</Field> </Field>
+10
View File
@@ -19,6 +19,16 @@ export function formatDuration(seconds?: number | null): string {
return toFa(`${m}:${String(s).padStart(2, "0")}`); return toFa(`${m}:${String(s).padStart(2, "0")}`);
} }
// Derive a clean title from an uploaded file name: drop the extension and turn
// underscores/dashes into spaces (e.g. "morning_calm-01.mp3" -> "morning calm 01").
export function fileNameToTitle(name: string): string {
return name
.replace(/\.[^./\\]+$/, "")
.replace(/[_-]+/g, " ")
.replace(/\s+/g, " ")
.trim();
}
// Minutes -> Persian label. Media and music store `duration` in minutes. // Minutes -> Persian label. Media and music store `duration` in minutes.
export function formatMinutes(minutes?: number | null): string { export function formatMinutes(minutes?: number | null): string {
if (minutes === null || minutes === undefined) return "—"; if (minutes === null || minutes === undefined) return "—";