. */ declare(strict_types=1); const FOLDER_PATH = '../video/unwatched'; const MAX_RECURSE_DEPTH = 5; $g_errors = []; class path_entry { public $base_name = ''; public $full_path = ''; public $depth = 0; public $media_type = 'unknown'; // 'movie' or 'tv_show' or 'unknown' public $path_type = ''; // 'file' or 'directory' public $file_extension = ''; public $content_size = 0; public $contents = []; // directory contents, array of path_entry objects public $parent = null; // reference to parent path_entry } function guessMediaType($filename) : string { $current_year = getdate()['year']; if (false !== stristr($filename, 'season')) { return 'tv_show'; } else if (preg_match('/s[0-9]{2}/i', $filename)) { return 'tv_show'; // TODO: this will only match the first 4 digit number in the string } else if (preg_match('/[0-9]{4}/', $filename, $match)) { if ((int) $match[0] >= 1878 && (int) $match[0] <= $current_year) return 'movie'; } return 'unknown'; } // NOTE: use parent reference to recursively set media types detected by children nodes function setMediaType(&$path_entry, $media_type) : void { // NOTE: don't set media type of top-level directory if ($path_entry->depth > 0) { $path_entry->media_type = $media_type; if ($path_entry->parent) setMediaType($path_entry->parent, $media_type); } } function getPathEntry($fs_path, $depth = 0, $media_type = '', &$parent = null) : ?path_entry { $pe = new path_entry(); $pe->base_name = basename($fs_path); $pe->full_path = $fs_path; $pe->depth = $depth; if ($parent !== null) $pe->parent =& $parent; if ($depth != 0 && ($media_type == '' || $media_type == 'unknown')) setMediaType($pe, guessMediaType($pe->base_name)); else $pe->media_type = $media_type; if (is_dir($fs_path) && is_readable($fs_path)) { $pe->path_type = 'directory'; if ($depth <= MAX_RECURSE_DEPTH) { foreach (scandir($fs_path) as $path) { if ($path != '.' && $path != '..') { $new_entry = getPathEntry($fs_path . '/' . $path, $depth + 1, $pe->media_type, $pe); if ($new_entry !== null) $pe->contents[] = $new_entry; } } } $pe->content_size = count($pe->contents); } else if (is_file($fs_path)) { $pe->path_type = 'file'; $pe->file_extension = pathinfo($fs_path, PATHINFO_EXTENSION); if (!in_array($pe->file_extension, ['avi', 'mkv', 'm4v', 'mp4', 'mpg'])) return null; } else { global $g_errors; $g_errors[] = __FUNCTION__ . ", invalid path: $fs_path"; return null; } return $pe; } function unsetParentReferences(&$path_entry) : void { unset($path_entry->parent); foreach ($path_entry->contents as &$entry) unsetParentReferences($entry); } function getUnsavedMetadata() : void { $path_info = getPathEntry(FOLDER_PATH); global $g_errors; $out = new StdClass(); header('Content-Type: text/json'); if (count($g_errors) == 0) { unsetParentReferences($path_info); // NOTE: don't encode parent references $out->mem_usage = memory_get_peak_usage(); $out->path_info = $path_info; $json = json_encode($out); if ($json) echo $json; else echo '{"errors":"error enconding json in ' . __FUNCTION__ . '"}'; } else { $out->errors = $g_errors; echo json_encode($out); } } function main() : void { switch (@$_GET['action']) { case 'scan': getUnsavedMetadata(); break; case 'file_map'; // break; } } main(); ?>