$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'], ]; if (!isset($directories[$type])) { echo json_encode(['error' => 'Invalid media type']); exit; } $targetDir = $directories[$type]; $validExtensions = $extensionsByType[$type]; // Fail fast if dir missing if (!is_dir($targetDir)) { echo json_encode(['error' => 'Directory not found']); exit; } /** * Recursively scan a directory and build a folder->(folders/files) structure. * Files are returned as paths relative to $baseDir. */ function scanDirectory(string $dir, string $baseDir, array $validExtensions): array { $result = []; // 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; } continue; } $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION)); if (!in_array($ext, $validExtensions, true)) { continue; } // 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; } // 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); }); return $result; } $tree = scanDirectory($targetDir, $baseDir, $validExtensions); echo json_encode( empty($tree) ? ['message' => 'No media files found'] : $tree, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );