Files
Yuba/Yuba.php

827 lines
22 KiB
PHP
Raw Normal View History

2017-05-03 00:39:48 -07:00
#!/usr/bin/php
<?php
2017-06-20 02:05:52 -07:00
// Yuba
// //
//////////////////////////////////////////
2017-06-20 02:36:17 -07:00
$version = "0.6.0.1";
2017-06-20 02:05:52 -07:00
ini_set('memory_limit', '4096M');
2017-05-25 15:38:19 -07:00
date_default_timezone_set("America/Los_Angeles");
2017-06-16 00:14:32 -07:00
$time_start = microtime(true);
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
include('ProgressBar.php');
// Path & application variables
2017-05-20 20:03:50 -07:00
//////////////////////////////////////////
2017-06-20 02:05:52 -07:00
if (in_array("-nohash", $argv)) { $wopt_hash = 0; } else { $wopt_hash = 1; }
if (in_array("-nothumbs", $argv)) { $wopt_thumbs = 0; } else { $wopt_thumbs = 1; }
$zpath = realpath(@$argv[1]);
$bdest = realpath(@$argv[2]);
if (!is_dir($zpath) | !is_dir($bdest)) { echo "Usage: walk <path> <dest>"; die; }
// Check for bundle
if ($zpath == "/") { $blabel = "root"; } else { $blabel = preg_replace("/[^A-Za-z0-9\.]/", "_", basename($zpath)); }
if (is_writable($zpath)) { $wopt_paranoid = 1; } else { $wopt_paranoid = 0; }
$bpath = chop($bdest,"/")."/".substr(crc32($zpath),0,3)."_".$blabel.".bundle";
if (!is_dir($bpath)) { mkdir($bpath); }
if (!is_dir($bpath."/thumbs")) { mkdir($bpath."/thumbs"); }
$wopt_hash_limit = 1; // don't hash if exceeds in gigs, 0 for unlimited
$wopt_thumb_size = "512";
2017-05-25 15:38:19 -07:00
2017-06-16 00:14:32 -07:00
// Treat these directories as files
$wopt_bundles = array( "app",
"bundle",
"sparsebundle",
"photoslibrary",
"aplibrary",
"apvault",
"abbu",
"calendar",
"framework",
"plugin",
"kext",
"rtfd"
);
foreach ($wopt_bundles as $bundle) {
$wopt_nodescend[] = "*.".$bundle;
}
2017-05-25 15:38:19 -07:00
// Ignore matching files and directories
2017-06-16 00:14:32 -07:00
$wopt_ignore = array( ".DS_Store",
".DocumentRevisions-V100",
".Spotlight-V100",
".TemporaryItems",
".apdisk",
".com.apple.timemachine.donotpresent",
".fseventsd",
".metadata-never-index",
".neofinder.abemeda.volinfo.xml"
);
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
$max_label = 50;
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// Metadata tools
2017-05-25 15:38:19 -07:00
$bin_gfi = "/Applications/Xcode.app/Contents/Developer/usr/bin/GetFileInfo";
$bin_mediainfo = "/opt/local/bin/mediainfo";
$bin_exiftool = "/opt/local/bin/exiftool";
2017-06-20 02:05:52 -07:00
$bin_tq = "/opt/local/bin/ql-thumbnail";
$bin_tv = "/opt/local/bin/vipsthumbnail";
$bin_tf = "/usr/local/bin/ffmpegthumbnailer";
// Media extensions
//////////////////////////////////////////
$t_files['ffmpeg'] = array( "mkv",
"avi",
"mpeg",
"mpg",
"vob",
"mp4",
"m4v",
"m2v",
"m2ts",
"asf",
"wmv",
"rm",
"divx",
"fla",
"flv",
"webm" );
$t_files['vips'] = array( "jpg",
"jpeg",
"tif",
"tiff",
"gif",
"psd",
"png" );
$m_files = array( "mkv",
"ogg",
"avi",
"wav",
"mpeg",
"mpg",
"vob",
"mp4",
"m2v",
"mp3",
"asf",
"wma",
"wmv",
"qt",
"mov",
"rm",
"ifo",
"ac3",
"dts",
"aac",
"ape",
"flac",
"aiff",
"m2ts" );
$e_files = array( "ai",
"aiff",
"ape",
"asf",
"avi",
"bmp",
"divx",
"dng",
"doc",
"docx",
"eps",
"epub",
"exe",
"exif",
"fla",
"flac",
"flv",
"gif",
"icc",
"iso",
"jpg",
"jpeg",
"m2ts",
"m4a",
"m4b",
"m4v",
"mkv",
"mobi",
"azw",
"azw3",
"mov",
"qt",
"mp3",
"mp4",
"mpeg",
"mpg",
"m2v",
"nef",
"numbers",
"ogg",
"pages",
"pdf",
"pict",
"png",
"ppm",
"ppt",
"psd",
"psb",
"qif",
"raw",
"rtf",
"sr2",
"srf",
"svg",
"swf",
"tiff",
"tif",
"torrent",
"vcf",
"vob",
"wav",
"webm",
"wma",
"wmv",
"xls",
"xlsx",
"xmp",
"zip" );
foreach ($e_files as $ext) { $e_files[] = strtoupper($ext); }
foreach ($m_files as $ext) { $m_files[] = strtoupper($ext); }
foreach ($t_files['ffmpeg'] as $ext) { $t_files['ffmpeg'][] = strtoupper($ext); }
foreach ($t_files['vips'] as $ext) { $t_files['vips'][] = strtoupper($ext); }
2017-05-25 15:38:19 -07:00
// Functions
2017-06-20 02:05:52 -07:00
//////////////////////////////////////////
/*
function getParents($zpath, $pathname) {
$path = dirname($pathname);
$parts = explode("/",trim(substr($path,strlen(basename($zpath))),"/"));
foreach ($parts as $index => $part) {
$parents[] = array($part, md5($zpath."/".implode("/",array_slice($parts, 0, $index+1))));
}
return $parents;
}
*/
function shortlabel($pathname, $max) {
$basename = basename($pathname);
$suffix = "(...).".pathinfo($basename,PATHINFO_EXTENSION);
if (strlen($basename) > $max) {
return substr($basename, 0, ($max-strlen($suffix))).$suffix;
} else {
return $basename;
}
}
function human_filesize($bytes, $decimals = 2) {
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor];
}
2017-05-25 15:38:19 -07:00
function stringPrint($string) {
2017-06-16 00:14:32 -07:00
echo $string.@str_repeat(" ", (10-strlen($string)));
2017-05-25 15:38:19 -07:00
}
function getWoptString() {
2017-06-20 02:05:52 -07:00
global $wopt_bundles, $wopt_ignore, $wopt_hash, $wopt_hash_limit, $wopt_mediainfo, $wopt_exiftool, $wopt_thumbs, $wopt_thumb_size, $wopt_paranoid;
return array( array("bundles", $wopt_bundles),
2017-05-25 15:38:19 -07:00
array("ignore", $wopt_ignore),
array("hash", $wopt_hash),
2017-06-20 02:05:52 -07:00
array("wopt_hash_limit", $wopt_hash_limit),
2017-05-25 15:38:19 -07:00
array("mediainfo", $wopt_mediainfo),
array("exiftool", $wopt_exiftool),
2017-06-20 02:05:52 -07:00
array("thumbs", $wopt_thumbs),
2017-05-25 15:38:19 -07:00
array("thumb_size", $wopt_thumb_size),
2017-06-20 02:05:52 -07:00
array("wopt_paranoid", $wopt_paranoid),
2017-05-25 15:38:19 -07:00
);
}
class plistParser extends XMLReader {
public function parseString($string) { $this->XML($string); return $this->process(); }
private function process() {
$this->read();
if($this->nodeType !== XMLReader::DOC_TYPE || $this->name !== "plist") { throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 2); }
if(!$this->next("plist") || $this->nodeType !== XMLReader::ELEMENT || $this->name !== "plist") { throw new Exception(sprintf("Error parsing plist. nodeType: %d -- Name: %s", $this->nodeType, $this->name), 3); }
$plist = array(); while($this->read()) { if($this->nodeType == XMLReader::ELEMENT) { $plist[] = $this->parse_node(); } }
if(count($plist) == 1 && $plist[0]) { return $plist[0]; } else { return $plist; }
}
private function parse_node() {
if($this->nodeType !== XMLReader::ELEMENT) return;
switch($this->name) {
case 'data': return base64_decode($this->getNodeText()); break;
case 'real': return floatval($this->getNodeText()); break;
case 'string': return $this->getNodeText(); break;
case 'integer': return intval($this->getNodeText()); break;
case 'date': return $this->getNodeText(); break;
case 'true': return true; break;
case 'false': return false; break;
case 'array': return $this->parse_array(); break;
case 'dict': return $this->parse_dict(); break;
default: throw new Exception(sprintf("Not a valid plist. %s is not a valid type", $this->name), 4);
}
}
private function parse_dict() {
$array = array(); $this->nextOfType(XMLReader::ELEMENT);
do { if($this->nodeType !== XMLReader::ELEMENT || $this->name !== "key") { if(!$this->next("key")) { return $array; } } $key = $this->getNodeText(); $this->nextOfType(XMLReader::ELEMENT); $array[$key] = $this->parse_node(); $this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT); }
while($this->nodeType && !$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "dict")); return $array;
}
private function parse_array() {
$array = array(); $this->nextOfType(XMLReader::ELEMENT);
do { $array[] = $this->parse_node(); $this->nextOfType(XMLReader::ELEMENT, XMLReader::END_ELEMENT); }
while($this->nodeType && !$this->isNodeOfTypeName(XMLReader::END_ELEMENT, "array")); return $array;
}
private function getNodeText() { $string = $this->readString(); $this->nextOfType(XMLReader::END_ELEMENT); return $string; }
private function nextOfType() { $types = func_get_args(); $this->read(); while($this->nodeType && !(in_array($this->nodeType, $types))) { $this->read(); } }
private function isNodeOfTypeName($type, $name) { return $this->nodeType === $type && $this->name === $name; }
}
function parseMediaInfo ($xml) {
$xml = simplexml_load_string($xml);
$data = array();
$data['version'] = (string) $xml['version'];
foreach ($xml->File->track as $track) {
$trackType = strtolower($track['type']);
$trackId = isset($track['streamid']) ? $track['streamid'] : 1;
$trackId = (string)$trackId;
$trackData = [];
foreach ($track as $rawKey => $rawVal) {
$key = strtolower($rawKey);
$val = (string)$rawVal;
if ($key == 'stream_identifier') { continue; }
if (!array_key_exists($key, $trackData)) {
$trackData[$key] = array($val);
} elseif (!in_array($val, $trackData[$key])) {
$trackData[$key][] = $val;
}
}
if ($trackType == 'general') {
$data['file']['general'] = $trackData;
} else {
$data['file'][$trackType][$trackId] = $trackData;
}
}
return $data;
}
function bashcolor($str,$fgcolor="white",$bgcolor=null) {
static $fgcolors = array('black' => '0;30', 'dark gray' => '1;30', 'blue' => '0;34', 'light blue' => '1;34', 'green' => '0;32', 'light green' => '1;32', 'cyan' => '0;36', 'light cyan' => '1;36', 'red' => '0;31', 'light red' => '1;31', 'purple' => '0;35', 'light purple' => '1;35', 'brown' => '0;33', 'yellow' => '1;33', 'light gray' => '0;37', 'white' => '1;37', 'underline' => '4');
static $bgcolors = array('black' => '40', 'red' => '41', 'green' => '42', 'yellow' => '43', 'blue' => '44', 'magenta' => '45', 'cyan' => '46', 'light gray' => '47');
$out="";
if (!isset($fgcolors[$fgcolor])) { $fgcolor='white'; }
if (!isset($bgcolors[$bgcolor])) { $bgcolor=null; }
if ($fgcolor) { $out .= "\033[{$fgcolors[$fgcolor]}m"; }
if ($bgcolor) { $out .= "\033[{$bgcolors[$bgcolor]}m"; }
$out .= $str."\033[0m";
return $out;
}
2017-05-22 23:37:36 -07:00
// Disk info
2017-06-20 02:05:52 -07:00
//////////////////////////////////////////
2017-05-20 20:03:50 -07:00
$host = gethostname();
2017-05-22 23:37:36 -07:00
$disks = shell_exec("diskutil list 2>&1");
if (substr($zpath, 0, 9) != "/Volumes/") {
$zbase = "/";
} else {
$zparts = explode("/", $zpath);
$zbase = "/Volumes/".$zparts[2];
}
$diskutil = shell_exec("diskutil info ".$zbase." 2>&1");
$getstats = array( "Volume Name",
"Protocol",
"Volume UUID",
"Device Location",
"Volume Total Space",
"Volume Available Space",
"Level Type"
);
foreach ($getstats as $stat) {
preg_match("/(".$stat.":)(.*)(\n)/",$diskutil,$matches);
if (isset($matches[2])) {
if (substr($stat, -5, 5) == "Space") {
$pieces = explode(" ", trim($matches[2]));
$summary = $pieces[0]." ".$pieces[1];
$stats[$stat] = $summary;
} else {
$stats[$stat] = trim($matches[2]);
}
}
}
$dstats = serialize($stats);
if ($zpath == "/") {
$type = "Startup disk";
} elseif (strtolower($zpath) == strtolower("/Volumes/".$stats["Volume Name"])) {
if ($stats["Protocol"] == "Disk Image") {
$type = "Disk image";
} else {
$type = "External disk";
}
} else {
$type = "Folder";
}
$profile = shell_exec("system_profiler SPHardwareDataType SPStorageDataType SPThunderboltDataType SPUSBDataType 2>&1");
2017-06-16 00:14:32 -07:00
$qlmanage = shell_exec("qlmanage -m 2>&1");
$sysvers = shell_exec("sw_vers 2>&1");
2017-05-22 23:37:36 -07:00
// Database
2017-06-20 02:05:52 -07:00
//////////////////////////////////////////
2017-05-20 20:03:50 -07:00
2017-06-20 02:05:52 -07:00
$stamp = date("Y-m-d_H-i-s", time());
2017-06-16 00:14:32 -07:00
2017-06-20 02:05:52 -07:00
$dbo = new PDO("sqlite:".$bpath."/".$stamp.".sqlite");
2017-05-20 20:03:50 -07:00
$dbo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
2017-06-20 02:05:52 -07:00
/*
2017-06-18 03:17:42 -07:00
$dbo->query("PRAGMA page_size = 4096");
$dbo->query("PRAGMA cache_size = 10000");
$dbo->query("PRAGMA locking_mode = EXCLUSIVE");
$dbo->query("PRAGMA synchronous = NORMAL");
$dbo->query("PRAGMA journal_mode = WAL");
2017-06-20 02:05:52 -07:00
*/
2017-06-16 00:14:32 -07:00
2017-06-20 02:05:52 -07:00
$dbo->exec("CREATE TABLE _walkwalk (
version TEXT,
opts TEXT,
host TEXT,
uid INTEGER,
zpath TEXT,
bpath TEXT,
type TEXT,
passed_file INTEGER,
passed_dir INTEGER,
passed_link INTEGER,
passed_total INTEGER,
nodescended INTEGER,
ignored INTEGER,
dupes INTEGER,
stats TEXT,
qlmanage TEXT,
sysvers TEXT,
diskutil TEXT,
disks TEXT,
profile TEXT,
status TEXT
)");
$dbo->exec("CREATE TABLE family (
pid TEXT,
fid TEXT,
children TEXT
)");
2017-05-25 15:38:19 -07:00
$dbo->exec("CREATE TABLE files (
2017-06-20 02:05:52 -07:00
pid TEXT,
fid TEXT,
2017-05-25 15:38:19 -07:00
Pathname TEXT,
Path TEXT,
Filename TEXT,
Extension TEXT,
Type TEXT,
2017-06-16 00:14:32 -07:00
items INTEGER,
2017-06-18 03:17:42 -07:00
newest INTEGER,
2017-05-25 15:38:19 -07:00
stat TEXT,
LinkTarget TEXT,
RealPath TEXT,
Inode INTEGER,
Size INTEGER,
Perms INTEGER,
Owner TEXT,
ATime INTEGER,
MTime INTEGER,
CTime INTEGER,
gfi_type TEXT,
gfi_attr TEXT,
gfi_created TEXT,
2017-05-20 20:03:50 -07:00
hash TEXT,
2017-06-20 02:05:52 -07:00
tinfo TEXT,
2017-05-25 15:38:19 -07:00
hasmeta INTEGER,
DateAdded TEXT,
ContentType TEXT,
Creator TEXT,
Kind TEXT,
UserTags TEXT,
FSInvisible INTEGER,
PixelWidth INTEGER,
PixelHeight INTEGER,
2017-06-20 02:05:52 -07:00
spotlight TEXT,
2017-05-25 15:38:19 -07:00
duration TEXT,
2017-05-20 20:03:50 -07:00
mediainfo TEXT,
2017-05-25 15:38:19 -07:00
exiftool TEXT
)");
2017-05-22 23:37:36 -07:00
2017-06-20 02:05:52 -07:00
$stmt = $dbo->prepare("INSERT INTO _walkwalk VALUES (:version, :opts, :host, :uid, :zpath, :bpath, :type, :passed_file, :passed_dir, :passed_link, :passed_total, :nodescended, :ignored, :dupes, :stats, :qlmanage, :sysvers, :diskutil, :disks, :profile, :status)");
$stmt->BindValue(":version",$version);
2017-05-25 15:38:19 -07:00
$stmt->BindValue(":opts",serialize(getWoptString()));
2017-05-20 20:03:50 -07:00
$stmt->BindValue(":host",$host);
2017-06-20 02:05:52 -07:00
$stmt->BindValue(":uid",posix_getuid());
2017-05-22 23:37:36 -07:00
$stmt->BindValue(":zpath",$zpath);
2017-06-20 02:05:52 -07:00
$stmt->BindValue(":bpath",$bpath);
2017-05-22 23:37:36 -07:00
$stmt->BindValue(":type",$type);
$stmt->BindValue(":stats",$dstats);
2017-06-16 00:14:32 -07:00
$stmt->BindValue(":qlmanage",$qlmanage);
$stmt->BindValue(":sysvers",$sysvers);
2017-05-22 23:37:36 -07:00
$stmt->BindValue(":diskutil",$diskutil);
2017-05-20 20:03:50 -07:00
$stmt->BindValue(":disks",$disks);
2017-05-22 23:37:36 -07:00
$stmt->BindValue(":profile",$profile);
2017-05-25 15:38:19 -07:00
$stmt->BindValue(":status","aborted");
2017-05-20 20:03:50 -07:00
$stmt->execute();
2017-06-20 02:05:52 -07:00
// Iterator
//////////////////////////////////////////
2017-05-03 00:39:48 -07:00
2017-06-20 02:05:52 -07:00
$passed_file = $passed_dir = $passed_link = $nodescended = $ignored = 0;
2017-05-25 15:38:19 -07:00
$files = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator(
2017-06-16 00:14:32 -07:00
// start in parent dir to include self
dirname($zpath),
2017-05-25 15:38:19 -07:00
RecursiveDirectoryIterator::SKIP_DOTS
),
function ($current, $key, $iterator) use ($wopt_ignore, $wopt_nodescend) {
2017-06-20 02:05:52 -07:00
global $nodescended, $ignored, $passed_file, $passed_dir, $passed_link, $zpath;
2017-05-25 15:38:19 -07:00
$clean = true;
2017-06-16 00:14:32 -07:00
// ensure we don't traverse zpath siblings
if ($zpath != "/" && (substr($current->getRealpath(), 0, strlen($zpath)+1) != $zpath."/") && ($current->getRealpath() != $zpath)) {
$clean = false;
}
// filenames to ignore
2017-05-25 15:38:19 -07:00
if (is_array($wopt_ignore)) {
foreach ($wopt_ignore as $wildcard) {
if (fnmatch($wildcard, $current->getFilename())) {
$clean = false;
$ignored++;
2017-06-20 02:05:52 -07:00
//echo "\nSkipping: ".$current->getRealpath()."\n\n";
2017-05-25 15:38:19 -07:00
}
}
}
2017-06-16 00:14:32 -07:00
// directories to ignore
2017-05-25 15:38:19 -07:00
if (is_array($wopt_nodescend)) {
foreach ($wopt_nodescend as $wildcard) {
if (fnmatch($wildcard, $current->getPath())) {
$clean = false;
$nodescended++;
2017-06-20 02:05:52 -07:00
//echo "\nNodescending: ".$current->getRealpath()."\n\n";
2017-05-25 15:38:19 -07:00
}
}
}
2017-06-20 02:05:52 -07:00
if ($clean) {
if ($current->getType() == "file") {
$passed_file++;
} elseif ($current->getType() == "dir") {
$passed_dir++;
} elseif ($current->getType() == "link") {
$passed_link++;
}
}
2017-05-25 15:38:19 -07:00
return $clean;
}
),
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD
);
2017-06-20 02:05:52 -07:00
// Banner
//////////////////////////////////////////
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
echo "Yuba ".$version."\n";
echo "-----------------------------------------------\n";
$banner = $zpath." -> ".$bpath;
echo $banner."\n";
echo str_repeat("-", strlen($banner))."\n";
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// Permissions
//////////////////////////////////////////
2017-05-22 23:37:36 -07:00
2017-06-20 02:05:52 -07:00
/*
2017-05-22 23:37:36 -07:00
if (posix_getuid()) {
2017-06-16 00:14:32 -07:00
echo bashcolor("You are not root. Checking file readability: ", "red");
2017-05-22 23:37:36 -07:00
2017-06-18 03:17:42 -07:00
echo "\n";
2017-05-22 23:37:36 -07:00
$oops = 0;
2017-05-25 15:38:19 -07:00
foreach ($files as $splFileInfo) {
2017-05-22 23:37:36 -07:00
$path = $splFileInfo->getRealPath();
if (!is_readable($path)) {
$oops = 1;
echo "x";
} else {
echo ".";
}
}
echo "\n\n";
if ($oops) {
echo "Some files could not be read. Continue? (Y/n)";
$line = trim(fgets(fopen("php://stdin","r")));
$line = $line ?: "y";
if($line != "y"){
echo "Exiting!\n"; die;
}
}
2017-06-16 00:14:32 -07:00
} else {
echo bashcolor("Running as root. Some QuickLook plugins may not be available.", "red");
2017-06-20 02:05:52 -07:00
echo "\n\n";
2017-06-18 03:17:42 -07:00
2017-05-22 23:37:36 -07:00
}
2017-06-20 02:05:52 -07:00
*/
2017-05-22 23:37:36 -07:00
2017-06-18 03:17:42 -07:00
$fixatimes = 0;
if ($wopt_paranoid) {
echo bashcolor("\nFilesystem is writable. You can choose:\n(c) Preserve ctimes (default)\n(a) Preserve atimes\n", "purple");
$line = trim(fgets(fopen("php://stdin","r"))) ?: "c";
if ($line == "a") { $fixatimes = 1; }
}
2017-06-16 00:14:32 -07:00
2017-06-20 02:05:52 -07:00
// Prescan
//////////////////////////////////////////
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
$i = 0;
$family = array();
$fids = array();
2017-05-25 15:38:19 -07:00
foreach ($files as $splFileInfo) {
$pathname = $splFileInfo->getPathname();
$path = $splFileInfo->getPath();
2017-06-20 02:05:52 -07:00
$key = md5($pathname);
$pkey = md5($path);
if (array_key_exists($key, $family)) {
echo "Duplicate key on ".$pathname."\n"; die;
2017-05-20 20:03:50 -07:00
}
2017-06-16 00:14:32 -07:00
2017-06-20 02:05:52 -07:00
$family[$key] = array();
// Path-agnostic Unique File ID (to prevent redundant hashes and thumbs)
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
if ($splFileInfo->getType() != "dir" && $splFileInfo->getType() != "link") {
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
$fid = md5($splFileInfo->getSize().$splFileInfo->getMtime().$splFileInfo->getBasename());
$dx[$fid][] = $pathname;
$fx[] = array($fid, $pathname);
$family[$key]['fid'] = $fid;
2017-05-03 00:39:48 -07:00
}
2017-06-20 02:05:52 -07:00
// Parents
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
//$family[$key]['parents'] = getParents($zpath, $pathname);
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// Children
2017-06-18 03:17:42 -07:00
2017-06-20 02:05:52 -07:00
if (strlen($path) > strlen($zpath)-1) { // prevent zpath root orphan
$family[$pkey]['children'][] = $key;
2017-06-18 03:17:42 -07:00
}
2017-06-20 02:05:52 -07:00
if ($i % 5000 == 0) {
echo "\r\033[K\rPrescan: ".$pathname;
2017-06-18 03:17:42 -07:00
}
2017-06-20 02:05:52 -07:00
$i++;
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
}
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
echo "\r\033[K\rPrescan: done\n";
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// Debug record of duplicate FIDs
$dupes = array_filter($dx, function($a) { return count($a) > 2; });
ob_start();
var_dump($dupes);
$dxo = ob_get_clean();
if (strlen($dxo)) {
file_put_contents($bpath."/".$stamp."_dupes.txt",$dxo);
$dupecount = count($dupes,COUNT_RECURSIVE) - count($dupes);
echo "\n".bashcolor(floor(($dupecount/$i)*100)." percent of files look like duplicates","green")."\n\n";
}
// Write family to DB
$message = "Writing family to DB: ";
$message .= $passed_file." files, ";
$message .= $passed_dir." dirs, ";
$message .= $nodescended." bundles, ";
$message .= $passed_link." links, ";
$message .= $ignored." ignored, ";
$message .= ($dupecount ? $dupecount : 0)." dupes";
echo ProgressBar::start($i,$message);
foreach ($family as $key => $item) {
echo ProgressBar::next();
$stmt = $dbo->prepare("INSERT INTO family VALUES (:pid, :fid, :children)");
$stmt->BindValue(":pid",$key);
if (@$item['fid']) {
$stmt->BindValue(":fid",$item['fid']);
2017-05-22 23:37:36 -07:00
}
2017-06-20 02:05:52 -07:00
if (@$item['children'] && is_array(@$item['children'])) {
$stmt->BindValue(":children",serialize($item['children']));
2017-05-03 00:39:48 -07:00
}
2017-05-25 15:38:19 -07:00
$stmt->execute();
2017-06-20 02:05:52 -07:00
}
echo ProgressBar::finish();
unset($dx, $dxo, $dupes);
// stats
$stmt = "UPDATE _walkwalk SET ";
$stmt .= "passed_file=".$passed_file.", ";
$stmt .= "passed_dir=".$passed_dir.", ";
$stmt .= "passed_link=".$passed_link.", ";
$stmt .= "passed_total=".$i.", ";
$stmt .= "nodescended=".$nodescended.", ";
$stmt .= "ignored=".$ignored.", ";
$stmt .= "dupes=".($dupecount ? $dupecount : 0).", ";
$stmt .= "status='completed_in_".$seconds."'";
$dbo->exec($stmt);
// Thumbnails
//////////////////////////////////////////
if ($wopt_thumbs) {
$message = "Generating thumbnails...";
echo ProgressBar::start(count($fx),$message);
$tempdir = "/tmp/".$blabel."_".$stamp;
if (!is_dir($tempdir)) { mkdir($tempdir); }
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
foreach ($fx as $array) {
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
$fid = $array[0];
$pathname = $array[1];
$ext = pathinfo($pathname,PATHINFO_EXTENSION);
$tpath = $bpath."/thumbs/".substr($fid, 0, 2);
$tfile = $tpath."/".$fid.".jpg";
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// HACK for ql-thumbnail bug
$t_skip = array("emlx");
if (count($t_skip) && in_array($ext, $t_skip)) {
echo ProgressBar::next(1, "Skipping ".shortlabel(basename($pathname),$max_label));
continue;
}
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// check to see if a thumb already exists
if (file_exists($tfile)) {
echo ProgressBar::next(1, "Thumb found for ".shortlabel(basename($pathname),$max_label));
continue;
2017-05-25 15:38:19 -07:00
} else {
2017-06-20 02:05:52 -07:00
echo ProgressBar::next(1, "Generating thumb for ".shortlabel(basename($pathname),$max_label));
2017-05-25 15:38:19 -07:00
}
2017-06-18 03:17:42 -07:00
2017-06-20 02:05:52 -07:00
$shellpath = escapeshellarg($pathname);
$tempfile = $tempdir."/".$fid.".jpg";
// first try to make a thumb with external tools
$cmd = null;
if (in_array($ext, $t_files['vips'])) {
$cmd = $bin_tv." ".$shellpath." -o ".$tempfile."[Q=85,optimize_coding] --size=".$wopt_thumb_size;
} elseif (in_array($ext, $t_files['ffmpeg'])) {
$cmd = $bin_tf." -i ".$shellpath." -o ".$tempfile." -s ".$wopt_thumb_size." -c jpg -q 9";
}
if ($cmd) { shell_exec($cmd." 2>&1"); }
// if those tools failed, try quicklook
if (!@filesize($tempfile)) {
$cmd = $bin_tq." ".$shellpath." ".$tempfile." public.jpeg-2000 ".$wopt_thumb_size." ".$wopt_thumb_size;
shell_exec($cmd." 2>&1");
}
// success, move thumb into the bundle
if (file_exists($tempfile) && @filesize($tempfile)) {
if (!is_dir($tpath)) { mkdir($tpath); }
rename($tempfile,$tfile);
}
2017-05-25 15:38:19 -07:00
2017-06-18 03:17:42 -07:00
}
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
echo ProgressBar::finish();
}
// Hashes
//////////////////////////////////////////
if ($wopt_hash) {
$dbh = new PDO("sqlite:".$bpath."/md5.sqlite");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->exec("CREATE TABLE IF NOT EXISTS hash (fid TEXT, hash TEXT)");
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
if ($wopt_hash_limit) {
2017-06-20 02:36:17 -07:00
$message = "Generating hashes for files under ".$wopt_hash_limit."GB";
2017-06-20 02:05:52 -07:00
} else {
$message = "Generating hashes for all files";
}
2017-06-18 03:17:42 -07:00
2017-06-20 02:05:52 -07:00
echo ProgressBar::start(count($fx),$message);
foreach ($fx as $array) {
$fid = $array[0];
$pathname = $array[1];
$size = filesize($pathname);
$limit = $wopt_hash_limit*1000000000;
$check = $dbh->query("SELECT EXISTS(SELECT 1 FROM hash WHERE fid='".$fid."')")->fetch()[0];
if ($check) {
echo ProgressBar::next(1, "Hash already exists: ".shortlabel($pathname,$max_label));
} elseif ($wopt_hash_limit && $size > $limit) {
echo ProgressBar::next(1, "Too big to hash: ".shortlabel($pathname,$max_label)." (".human_filesize($size).")");
} else {
echo ProgressBar::next(1);
$stmt = $dbh->prepare("INSERT INTO hash VALUES (:fid, :hash)");
$stmt->BindValue(":fid",$fid);
$stmt->BindValue(":hash",md5_file($pathname));
$stmt->execute();
2017-06-18 03:17:42 -07:00
}
2017-05-25 15:38:19 -07:00
}
2017-06-20 02:05:52 -07:00
echo ProgressBar::finish();
2017-05-25 15:38:19 -07:00
2017-05-20 20:03:50 -07:00
}
2017-06-20 02:05:52 -07:00
// Files
//////////////////////////////////////////
2017-06-18 03:17:42 -07:00
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
// Cleanup
//////////////////////////////////////////
echo "\n";
$seconds = floor($time = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]);
$dbo->exec("UPDATE _walkwalk SET status='completed_in_".$seconds."'");
echo "Finished in ".$seconds." seconds\n\n";
2017-05-25 15:38:19 -07:00
2017-06-20 02:05:52 -07:00
unset($dbo, $dbh, $files, $family, $fx);
2017-05-03 00:39:48 -07:00
?>