This repository has been archived on 2026-06-15. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MediaLore/src/lib/files.ts
2026-04-05 10:01:34 -04:00

79 lines
2.3 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import type { DirectoryListing, FileEntry, MediaType } from '@/types'
import { resolveAndJail } from '@/lib/libraries'
const HIDDEN_FILES = /^\./
const VIDEO_EXTENSIONS = new Set(['.mp4', '.mov', '.mkv', '.avi', '.webm', '.m4v'])
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.tif'])
function getMediaType(filename: string): MediaType {
const ext = path.extname(filename).toLowerCase()
if (VIDEO_EXTENSIONS.has(ext)) return 'video'
if (IMAGE_EXTENSIONS.has(ext)) return 'image'
return 'other'
}
function fileApiUrl(libraryId: string, relativePath: string): string {
return `/api/file?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(relativePath)}`
}
function thumbnailApiUrl(libraryId: string, relativePath: string): string {
return `/api/thumbnail?libraryId=${encodeURIComponent(libraryId)}&path=${encodeURIComponent(relativePath)}`
}
export function scanDirectory(
libraryRoot: string,
libraryId: string,
subpath: string
): DirectoryListing {
let absPath: string
try {
absPath = subpath ? resolveAndJail(libraryRoot, subpath) : libraryRoot
} catch {
return { path: subpath, entries: [] }
}
let dirents: fs.Dirent[]
try {
dirents = fs.readdirSync(absPath, { withFileTypes: true })
} catch {
return { path: subpath, entries: [] }
}
const entries: FileEntry[] = dirents
.filter((d) => !HIDDEN_FILES.test(d.name))
.map((d): FileEntry => {
if (d.isDirectory()) {
return {
name: d.name,
type: 'directory',
mediaType: null,
url: null,
thumbnailUrl: null,
}
}
const relPath = subpath ? path.join(subpath, d.name) : d.name
const mediaType = getMediaType(d.name)
const hasThumbnail = mediaType === 'image' || mediaType === 'video'
return {
name: d.name,
type: 'file',
mediaType,
url: fileApiUrl(libraryId, relPath),
thumbnailUrl: hasThumbnail ? thumbnailApiUrl(libraryId, relPath) : null,
}
})
// Sort: directories first, then files; each group sorted alphabetically
entries.sort((a, b) => {
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1
return a.name.localeCompare(b.name)
})
return { path: subpath, entries }
}