freax-media/get_files.php

128 lines
3.6 KiB
PHP
Raw Permalink Normal View History

<?php
/**
* get_files.php - Recursively scans media directories and returns file structure as JSON
*/
2026-02-02 12:26:55 -05:00
declare(strict_types=1);
require_once __DIR__ . '/auth.php';
require_auth(true);
2026-02-02 12:26:55 -05:00
header('Content-Type: application/json; charset=utf-8');
2026-02-02 12:26:55 -05:00
// Media type from query parameter (default: videos)
$type = $_GET['type'] ?? 'videos';
// Resolve base directory via MEDIA_ROOT
$mediaRoot = getenv('MEDIA_ROOT');
if ($mediaRoot === false || trim($mediaRoot) === '') {
echo json_encode(['error' => 'MEDIA_ROOT is not set']);
exit;
}
$baseDir = realpath(trim($mediaRoot));
if ($baseDir === false || !is_dir($baseDir)) {
echo json_encode(['error' => 'MEDIA_ROOT base directory not found']);
exit;
}
$directories = [
2026-02-02 12:26:55 -05:00
'videos' => $baseDir . DIRECTORY_SEPARATOR . 'Videos',
'music' => $baseDir . DIRECTORY_SEPARATOR . 'Music',
];
// Extensions per tab (dont mix)
$extensionsByType = [
'videos' => ['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'm4v'],
'music' => ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac', 'wma', 'opus'],
];
if (!isset($directories[$type])) {
echo json_encode(['error' => 'Invalid media type']);
exit;
}
$targetDir = $directories[$type];
2026-02-02 12:26:55 -05:00
$validExtensions = $extensionsByType[$type];
2026-02-02 12:26:55 -05:00
// Fail fast if dir missing
if (!is_dir($targetDir)) {
2026-02-02 12:26:55 -05:00
echo json_encode(['error' => 'Directory not found']);
exit;
}
/**
2026-02-02 12:26:55 -05:00
* Recursively scan a directory and build a folder->(folders/files) structure.
* Files are returned as paths relative to $baseDir.
*/
2026-02-02 12:26:55 -05:00
function scanDirectory(string $dir, string $baseDir, array $validExtensions): array
{
$result = [];
2026-02-02 12:26:55 -05:00
// Resolve base once (prevents per-file realpath overhead and handles normalization)
$realBase = realpath($baseDir);
if ($realBase === false) {
return [];
}
$realBase = rtrim($realBase, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
2026-02-02 12:26:55 -05:00
// scandir() can return false on permissions
$items = @scandir($dir);
if ($items === false) return [];
foreach ($items as $item) {
// Skip dot/hidden entries
if ($item === '.' || $item === '..' || ($item !== '' && $item[0] === '.')) {
continue;
}
$fullPath = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($fullPath)) {
$sub = scanDirectory($fullPath, $baseDir, $validExtensions);
if (!empty($sub)) {
$result[$item] = $sub;
}
2026-02-02 12:26:55 -05:00
continue;
}
$ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
if (!in_array($ext, $validExtensions, true)) {
continue;
}
2026-02-02 12:26:55 -05:00
// Normalize and enforce: file must live under baseDir
2026-02-02 12:26:55 -05:00
$realFile = realpath($fullPath);
if ($realFile === false) {
2026-02-02 12:26:55 -05:00
continue;
}
if (strpos($realFile, $realBase) !== 0) {
2026-02-02 12:26:55 -05:00
continue;
}
// Return relative path from baseDir
$relativePath = substr($realFile, strlen($realBase));
2026-02-02 12:26:55 -05:00
$result[$item] = $relativePath;
}
2026-02-02 12:26:55 -05:00
// Sort: folders first, then files, alphabetically
uksort($result, function ($a, $b) use ($result) {
$aIsDir = is_array($result[$a]);
$bIsDir = is_array($result[$b]);
2026-02-02 12:26:55 -05:00
if ($aIsDir && !$bIsDir) return -1;
if (!$aIsDir && $bIsDir) return 1;
2026-02-02 12:26:55 -05:00
// IMPORTANT: keys can be ints if the filename is numeric (e.g. "01", "2026")
return strcasecmp((string)$a, (string)$b);
});
2026-02-02 12:26:55 -05:00
return $result;
}
2026-02-02 12:26:55 -05:00
$tree = scanDirectory($targetDir, $baseDir, $validExtensions);
2026-02-02 12:26:55 -05:00
echo json_encode(
empty($tree) ? ['message' => 'No media files found'] : $tree,
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
);