2026-02-02 12:14:18 -05:00
|
|
|
|
<?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);
|
2026-02-02 12:14:18 -05:00
|
|
|
|
|
2026-02-02 12:26:55 -05:00
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
2026-02-02 12:14:18 -05:00
|
|
|
|
|
2026-02-02 12:26:55 -05:00
|
|
|
|
// Media type from query parameter (default: videos)
|
|
|
|
|
|
$type = $_GET['type'] ?? 'videos';
|
|
|
|
|
|
|
|
|
|
|
|
// Resolve base directory (make this configurable via env in production)
|
|
|
|
|
|
$homeDir = getenv('HOME') ?: (getenv('USERPROFILE') ?: ('/home/' . get_current_user()));
|
|
|
|
|
|
$baseDir = rtrim($homeDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'GDrive';
|
2026-02-02 12:14:18 -05:00
|
|
|
|
|
|
|
|
|
|
$directories = [
|
2026-02-02 12:26:55 -05:00
|
|
|
|
'videos' => $baseDir . DIRECTORY_SEPARATOR . 'Videos',
|
|
|
|
|
|
'music' => $baseDir . DIRECTORY_SEPARATOR . 'Music',
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// Extensions per tab (don’t mix)
|
|
|
|
|
|
$extensionsByType = [
|
|
|
|
|
|
'videos' => ['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'm4v'],
|
|
|
|
|
|
'music' => ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac', 'wma', 'opus'],
|
2026-02-02 12:14:18 -05:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
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:14:18 -05:00
|
|
|
|
|
2026-02-02 12:26:55 -05:00
|
|
|
|
// Fail fast if dir missing
|
2026-02-02 12:14:18 -05:00
|
|
|
|
if (!is_dir($targetDir)) {
|
2026-02-02 12:26:55 -05:00
|
|
|
|
echo json_encode(['error' => 'Directory not found']);
|
2026-02-02 12:14:18 -05:00
|
|
|
|
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:14:18 -05:00
|
|
|
|
*/
|
2026-02-02 12:26:55 -05:00
|
|
|
|
function scanDirectory(string $dir, string $baseDir, array $validExtensions): array
|
|
|
|
|
|
{
|
2026-02-02 12:14:18 -05:00
|
|
|
|
$result = [];
|
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:14:18 -05:00
|
|
|
|
}
|
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:14:18 -05:00
|
|
|
|
}
|
2026-02-02 12:26:55 -05:00
|
|
|
|
|
|
|
|
|
|
// Build a relative path from baseDir. Use realpath() to normalize.
|
|
|
|
|
|
$realBase = realpath($baseDir);
|
|
|
|
|
|
$realFile = realpath($fullPath);
|
|
|
|
|
|
if ($realBase === false || $realFile === false) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Enforce: file must live under baseDir
|
|
|
|
|
|
if (strpos($realFile, $realBase . DIRECTORY_SEPARATOR) !== 0) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$relativePath = substr($realFile, strlen($realBase) + 1); // +1 skips the slash
|
|
|
|
|
|
$result[$item] = $relativePath;
|
2026-02-02 12:14:18 -05:00
|
|
|
|
}
|
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]);
|
|
|
|
|
|
|
|
|
|
|
|
if ($aIsDir && !$bIsDir) return -1;
|
|
|
|
|
|
if (!$aIsDir && $bIsDir) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
// IMPORTANT: keys can be ints if the filename is numeric (e.g. "01", "2026")
|
|
|
|
|
|
return strcasecmp((string)$a, (string)$b);
|
2026-02-02 12:14:18 -05:00
|
|
|
|
});
|
2026-02-02 12:26:55 -05:00
|
|
|
|
|
|
|
|
|
|
|
2026-02-02 12:14:18 -05:00
|
|
|
|
return $result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-02 12:26:55 -05:00
|
|
|
|
$tree = scanDirectory($targetDir, $baseDir, $validExtensions);
|
2026-02-02 12:14:18 -05:00
|
|
|
|
|
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
|
|
|
|
|
|
);
|
2026-02-02 12:14:18 -05:00
|
|
|
|
|