1128 lines
30 KiB
PHP
Executable File
1128 lines
30 KiB
PHP
Executable File
<?php
|
|
|
|
// Yuba
|
|
// //
|
|
//////////////////////////////////////////
|
|
$version = "0.6.9";
|
|
|
|
ini_set('memory_limit', '4096M');
|
|
date_default_timezone_set("America/Los_Angeles");
|
|
$time_start = microtime(true);
|
|
|
|
// Includes & Prefs
|
|
//////////////////////////////////////////
|
|
|
|
require("functions.php");
|
|
require("filetypes.php");
|
|
|
|
$wopt_noprofile = 1;
|
|
|
|
$p = unserialize(file_get_contents("prefs.php"));
|
|
|
|
// Path & application variables
|
|
//////////////////////////////////////////
|
|
|
|
if (!isset($argv[1])) { echo "Input error"; die; }
|
|
$zpath = realpath(@$argv[1]);
|
|
if (@$argv[2]) { $bdest = realpath($argv[2]); } else { $bdest = realpath($p['bdest']); }
|
|
if (!is_dir($zpath) | !is_dir($bdest)) { echo "Filepath error"; die; }
|
|
|
|
// Check for bundle
|
|
if ($zpath == "/") { $blabel = "root"; } else { $blabel = preg_replace("/[^A-Za-z0-9\.]/", "_", basename($zpath)); }
|
|
if (is_writable($zpath)) { $p['paranoid'] = 1; } else { $p['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"); }
|
|
|
|
// Treat these directories as files
|
|
$p['bundles'] = array( "app",
|
|
"bundle",
|
|
"sparsebundle",
|
|
"photoslibrary",
|
|
"aplibrary",
|
|
"apvault",
|
|
"abbu",
|
|
"calendar",
|
|
"framework",
|
|
"plugin",
|
|
"kext",
|
|
"rtfd"
|
|
);
|
|
|
|
foreach ($p['bundles'] as $bundle) {
|
|
$p['nodescend'][] = "*.".$bundle;
|
|
}
|
|
|
|
// Ignore matching files and directories
|
|
$p['ignore'] = array( ".DS_Store",
|
|
".DocumentRevisions-V100",
|
|
".Spotlight-V100",
|
|
".TemporaryItems",
|
|
".apdisk",
|
|
".com.apple.timemachine.donotpresent",
|
|
".fseventsd",
|
|
".metadata-never-index",
|
|
".neofinder.abemeda.volinfo.xml"
|
|
);
|
|
|
|
// Metadata tools
|
|
$bin_gfi = __DIR__."/bin/GetFileInfo";
|
|
$bin_mediainfo = __DIR__."/bin/mediainfo";
|
|
$bin_exiftool = __DIR__."/bin/exiftool";
|
|
$bin_ffmpeg = __DIR__."/bin/ffmpeg";
|
|
$bin_qlthumb = __DIR__."/bin/ql-thumbnail";
|
|
|
|
// Banner
|
|
//////////////////////////////////////////
|
|
|
|
$banner = "Yuba: ".$zpath." -> ".$bpath;
|
|
echo $banner."\n".str_repeat("-", strlen($banner))."\n";
|
|
|
|
// Disk info
|
|
//////////////////////////////////////////
|
|
|
|
echo "Gathering system info...\n";
|
|
|
|
$host = gethostname();
|
|
$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";
|
|
}
|
|
|
|
if ($wopt_noprofile) {
|
|
$profile = "disabled";
|
|
} else {
|
|
$profile = shell_exec("system_profiler SPHardwareDataType SPStorageDataType SPThunderboltDataType SPUSBDataType 2>&1");
|
|
}
|
|
$qlmanage = shell_exec("qlmanage -m 2>&1");
|
|
$sysvers = shell_exec("sw_vers 2>&1");
|
|
|
|
// Database
|
|
//////////////////////////////////////////
|
|
|
|
echo "Building database...\n";
|
|
|
|
$stamp = date("Y-m-d_H-i-s", time());
|
|
|
|
$dbo = new PDO("sqlite:".$bpath."/".$stamp.".sqlite3");
|
|
$dbo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
/*
|
|
$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");
|
|
*/
|
|
|
|
$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
|
|
)");
|
|
|
|
/*
|
|
|
|
Gather for each file:
|
|
|
|
A. IDs
|
|
|
|
pid (path ID)
|
|
fid (file ID)
|
|
|
|
B. splFileInfo methods
|
|
|
|
Pathname
|
|
Path
|
|
Filename
|
|
Extension
|
|
Type
|
|
Inode
|
|
Perms
|
|
Owner
|
|
ATime
|
|
CTime
|
|
MTime
|
|
LinkTarget
|
|
RealPath
|
|
|
|
C. External methods
|
|
|
|
stat
|
|
items
|
|
newest
|
|
gfi_type
|
|
gfi_attr
|
|
gfi_created
|
|
|
|
D. Aggregates
|
|
|
|
Size (splFileInfo, du)
|
|
Title (exiftool, kMDItemTitle, mediainfo)
|
|
PixelWidth (kMDItemPixelWidth, exiftool, mediainfo)
|
|
PixelHeight (kMDItemPixelHeight, exiftool, mediainfo)
|
|
Duration (kMDItemDurationSeconds, mediainfo, exiftool)
|
|
DateTimeOriginal (exiftool[DateTimeOriginal],mediainfo[EncodedDate],exiftool[CreateDate,MediaCreateDate],kMDItemContentCreationDate)
|
|
Origin (exiftool[CameraModelName,Producer,CreatorTool,WriterName,Software,Encoder],mediainfo[WritingApplication])
|
|
GPS (exiftool[GPSPosition], kMDItemLatitude.kMDItemLongitude)
|
|
Author (exiftool[Author,Artist,Creator,By-line])
|
|
|
|
E. Spotlight
|
|
|
|
spotlight (whole plist)
|
|
kMDItemDateAdded
|
|
kMDItemLastUsedDate
|
|
kMDItemUseCount
|
|
kMDItemContentModificationDate
|
|
kMDItemContentType
|
|
kMDItemCreator
|
|
kMDItemFSCreatorCode
|
|
kMDItemKind
|
|
kMDItemFSTypeCode
|
|
kMDItemUserTags
|
|
kMDItemFSInvisible
|
|
kMDItemNumberOfPages
|
|
kMDItemPageHeight
|
|
kMDItemPageWidth
|
|
kMDItemWhereFroms
|
|
kMDItemEncodingApplications
|
|
|
|
F. Pool
|
|
|
|
has_exif
|
|
has_mediainfo
|
|
has_hash
|
|
thumb_filename
|
|
thumb_width
|
|
thumb_height
|
|
|
|
G. Exiftool
|
|
|
|
ProfileDescription
|
|
BitDepth
|
|
Compression
|
|
WhiteBalance
|
|
Orientation
|
|
LensType
|
|
|
|
H. Mediainfo
|
|
|
|
VideoFormat
|
|
AudioFormat
|
|
Tracks
|
|
Profile
|
|
Bitrate
|
|
|
|
*/
|
|
|
|
$dbo->exec("CREATE TABLE files (
|
|
pid TEXT,
|
|
fid TEXT,
|
|
Pathname TEXT,
|
|
Path TEXT,
|
|
Filename TEXT,
|
|
Extension TEXT,
|
|
Type TEXT,
|
|
Inode INTEGER,
|
|
Perms INTEGER,
|
|
Owner TEXT,
|
|
ATime INTEGER,
|
|
CTime INTEGER,
|
|
MTime INTEGER,
|
|
LinkTarget TEXT,
|
|
RealPath TEXT,
|
|
stat TEXT,
|
|
items INTEGER,
|
|
newest INTEGER,
|
|
gfi_type TEXT,
|
|
gfi_attr TEXT,
|
|
gfi_created TEXT,
|
|
Size INTEGER,
|
|
Title TEXT,
|
|
PixelWidth INTEGER,
|
|
PixelHeight INTEGER,
|
|
Duration INTEGER,
|
|
DateTimeOriginal INTEGER,
|
|
Origin TEXT,
|
|
GPS TEXT,
|
|
Author TEXT,
|
|
spotlight TEXT,
|
|
kMDItemDateAdded INTEGER,
|
|
kMDItemLastUsedDate INTEGER,
|
|
kMDItemUseCount INTEGER,
|
|
kMDItemContentModificationDate INTEGER,
|
|
kMDItemContentType TEXT,
|
|
kMDItemCreator TEXT,
|
|
kMDItemFSCreatorCode TEXT,
|
|
kMDItemKind TEXT,
|
|
kMDItemFSTypeCode TEXT,
|
|
kMDItemUserTags TEXT,
|
|
kMDItemFSInvisible INTEGER,
|
|
kMDItemNumberOfPages INTEGER,
|
|
kMDItemPageHeight INTEGER,
|
|
kMDItemPageWidth INTEGER,
|
|
kMDItemWhereFroms TEXT,
|
|
kMDItemEncodingApplications TEXT,
|
|
has_exif INTEGER,
|
|
has_mediainfo INTEGER,
|
|
has_hash INTEGER,
|
|
thumb_filename TEXT,
|
|
thumb_width INTEGER,
|
|
thumb_height INTEGER,
|
|
ProfileDescription TEXT,
|
|
BitDepth INTEGER,
|
|
Compression TEXT,
|
|
Orientation INTEGER,
|
|
LensType TEXT,
|
|
VideoFormat TEXT,
|
|
AudioFormat TEXT,
|
|
Tracks INTEGER,
|
|
Profile TEXT,
|
|
Bitrate INTEGER
|
|
)");
|
|
|
|
$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);
|
|
$stmt->BindValue(":opts",serialize($p));
|
|
$stmt->BindValue(":host",$host);
|
|
$stmt->BindValue(":uid",posix_getuid());
|
|
$stmt->BindValue(":zpath",$zpath);
|
|
$stmt->BindValue(":bpath",$bpath);
|
|
$stmt->BindValue(":type",$type);
|
|
$stmt->BindValue(":stats",$dstats);
|
|
$stmt->BindValue(":qlmanage",$qlmanage);
|
|
$stmt->BindValue(":sysvers",$sysvers);
|
|
$stmt->BindValue(":diskutil",$diskutil);
|
|
$stmt->BindValue(":disks",$disks);
|
|
$stmt->BindValue(":profile",$profile);
|
|
$stmt->BindValue(":status","aborted");
|
|
$stmt->execute();
|
|
|
|
// Iterator
|
|
//////////////////////////////////////////
|
|
|
|
$first_run = 1;
|
|
$passed_file = $passed_dir = $passed_link = $passed_total = $nodescended = $ignored = 0;
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveCallbackFilterIterator(
|
|
new RecursiveDirectoryIterator(
|
|
$zpath,
|
|
RecursiveDirectoryIterator::SKIP_DOTS
|
|
),
|
|
function ($current, $key, $iterator) use ($p) {
|
|
global $nodescended, $ignored, $passed_file, $passed_dir, $passed_link, $passed_total, $first_run;
|
|
$clean = true;
|
|
// identify ignore files
|
|
if (is_array($p['ignore'])) {
|
|
foreach ($p['ignore'] as $wildcard) {
|
|
if (fnmatch($wildcard, $current->getFilename())) {
|
|
$clean = false;
|
|
if ($first_run) { $ignored++; }
|
|
}
|
|
}
|
|
}
|
|
// identify nodescend dirs
|
|
if (is_array($p['nodescend'])) {
|
|
foreach ($p['nodescend'] as $wildcard) {
|
|
if (fnmatch($wildcard, $current->getPath())) {
|
|
$clean = false;
|
|
if ($first_run) { $nodescended++; }
|
|
}
|
|
}
|
|
}
|
|
//tally stats
|
|
if ($clean && $first_run) {
|
|
if ($current->getType() == "file") {
|
|
$passed_file++;
|
|
} elseif ($current->getType() == "dir") {
|
|
$passed_dir++;
|
|
} elseif ($current->getType() == "link") {
|
|
$passed_link++;
|
|
}
|
|
$passed_total++;
|
|
}
|
|
return $clean;
|
|
}
|
|
),
|
|
RecursiveIteratorIterator::SELF_FIRST,
|
|
RecursiveIteratorIterator::CATCH_GET_CHILD
|
|
);
|
|
|
|
// Tally
|
|
//////////////////////////////////////////
|
|
|
|
foreach ($files as $null) { }
|
|
$first_run = 0;
|
|
|
|
// Permissions
|
|
//////////////////////////////////////////
|
|
|
|
if (posix_getuid()) {
|
|
|
|
echo ProgressBar::start($passed_total,"File permissions");
|
|
|
|
echo "You are not root. Checking file readability";
|
|
|
|
$oops = 0;
|
|
foreach ($files as $splFileInfo) {
|
|
$path = $splFileInfo->getRealPath();
|
|
if ($path && !is_readable($path)) {
|
|
$oops = 1;
|
|
echo "x";
|
|
} else {
|
|
echo ".";
|
|
}
|
|
echo ProgressBar::next();
|
|
//echo "\t".$path."\n";
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
echo "\n";
|
|
|
|
if ($oops) {
|
|
echo "Some files could not be read. Stopping.";
|
|
die;
|
|
}
|
|
|
|
} else {
|
|
|
|
echo "Running as root. Some QuickLook plugins may not be available.";
|
|
echo "\n";
|
|
|
|
}
|
|
|
|
if ($p['paranoid']) {
|
|
echo "Filesystem is writable. ";
|
|
if ($p['fixatimes']) {
|
|
echo "Preserving atimes.";
|
|
} else {
|
|
echo "Preserving ctimes.";
|
|
}
|
|
echo "\n";
|
|
}
|
|
|
|
// Pool DB
|
|
//////////////////////////////////////////
|
|
|
|
$dbp = new PDO("sqlite:".$bpath."/pool.sqlite3");
|
|
$dbp->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$dbp->exec("CREATE TABLE IF NOT EXISTS md5 (fid TEXT, hash TEXT)");
|
|
$dbp->exec("CREATE TABLE IF NOT EXISTS exiftool (fid TEXT, tags TEXT)");
|
|
$dbp->exec("CREATE TABLE IF NOT EXISTS mediainfo (fid TEXT, info TEXT)");
|
|
$dbp->exec("CREATE TABLE IF NOT EXISTS thumbs (fid TEXT, created INTEGER, relative_path TEXT, width INTEGER, height INTEGER, tool TEXT)");
|
|
|
|
// Prescan
|
|
//////////////////////////////////////////
|
|
|
|
$family = array();
|
|
$fids = array();
|
|
|
|
echo ProgressBar::start($passed_total,"Prescan");
|
|
|
|
foreach ($files as $splFileInfo) {
|
|
|
|
$pathname = $splFileInfo->getPathname();
|
|
$path = $splFileInfo->getPath();
|
|
|
|
$key = md5($pathname);
|
|
$pkey = md5($path);
|
|
|
|
if (array_key_exists($key, $family)) {
|
|
echo "Duplicate key on ".$pathname."\n"; die;
|
|
}
|
|
|
|
$family[$key] = array();
|
|
|
|
// Path-agnostic Unique File ID (to prevent redundant hashes and thumbs)
|
|
|
|
if ($splFileInfo->getType() != "dir" && $splFileInfo->getType() != "link") {
|
|
|
|
$fid = md5($splFileInfo->getSize().$splFileInfo->getMtime().$splFileInfo->getBasename());
|
|
$dx[$fid][] = $pathname;
|
|
$fx[] = array($fid, $pathname);
|
|
$family[$key]['fid'] = $fid;
|
|
|
|
}
|
|
|
|
// Parents
|
|
|
|
//$family[$key]['parents'] = getParents($zpath, $pathname);
|
|
|
|
// Children
|
|
|
|
$family[$pkey]['children'][] = $key;
|
|
|
|
echo ProgressBar::next();
|
|
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
// Debug record of duplicate FIDs
|
|
|
|
$dupes = array_filter($dx, function($a) { return count($a) > 1; });
|
|
$dxo = var_export($dupes, true);
|
|
|
|
if (strlen($dxo)) {
|
|
file_put_contents($bpath."/".$stamp."_dupes.txt",$dxo);
|
|
$dupecount = count($dupes,COUNT_RECURSIVE) - count($dupes);
|
|
echo floor(($dupecount/$passed_total)*100)." percent of files look like duplicates\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(count($family),$message);
|
|
|
|
foreach ($family as $key => $item) {
|
|
|
|
$stmt = $dbo->prepare("INSERT INTO family VALUES (:pid, :fid, :children)");
|
|
$stmt->BindValue(":pid",$key);
|
|
if (@$item['fid']) {
|
|
$stmt->BindValue(":fid",$item['fid']);
|
|
}
|
|
if (@$item['children'] && is_array(@$item['children'])) {
|
|
$stmt->BindValue(":children",serialize($item['children']));
|
|
}
|
|
$stmt->execute();
|
|
|
|
echo ProgressBar::next();
|
|
|
|
}
|
|
|
|
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=".$passed_total.", ";
|
|
$stmt .= "nodescended=".$nodescended.", ";
|
|
$stmt .= "ignored=".$ignored.", ";
|
|
$stmt .= "dupes=".($dupecount ? $dupecount : 0);
|
|
$dbo->exec($stmt);
|
|
|
|
// Thumbnails
|
|
//////////////////////////////////////////
|
|
|
|
if ($p['thumbs']) {
|
|
|
|
echo ProgressBar::start(count($fx),"Generating thumbnails");
|
|
|
|
foreach ($fx as $array) {
|
|
|
|
$fid = $array[0];
|
|
$pathname = $array[1];
|
|
$ext = pathinfo($pathname,PATHINFO_EXTENSION);
|
|
$tpath = $bpath."/thumbs/".substr($fid, 0, 2);
|
|
if (!is_dir($tpath)) { mkdir($tpath); }
|
|
$tfile = $tpath."/".$fid.".jpg";
|
|
|
|
// HACK for ql-thumbnail bug
|
|
$t_skip = array("emlx");
|
|
if (count($t_skip) && in_array($ext, $t_skip)) {
|
|
echo ProgressBar::next("Skipping ".shortlabel($pathname));
|
|
continue;
|
|
}
|
|
|
|
// if no thumb file, then poll database
|
|
if (file_exists($tfile)) {
|
|
echo ProgressBar::next("Thumb file found for ".shortlabel($pathname));
|
|
continue;
|
|
} elseif ($dbp->query("SELECT EXISTS(SELECT 1 FROM thumbs WHERE fid='".$fid."')")->fetch()[0]) {
|
|
echo ProgressBar::next("Thumb record found for ".shortlabel($pathname));
|
|
continue;
|
|
} else {
|
|
echo ProgressBar::next("Generating thumb for ".shortlabel($pathname));
|
|
}
|
|
|
|
$stmt = $dbp->prepare("INSERT INTO thumbs VALUES (:fid, :created, :relative_path, :width, :height, :tool)");
|
|
$stmt->BindValue(":fid",$fid);
|
|
$stmt->BindValue(":created",time());
|
|
|
|
$shellpath = escapeshellarg($pathname);
|
|
|
|
// first try to make a thumb with external tools
|
|
$cmd = null;
|
|
if (in_array($ext, $t_files['sips'])) {
|
|
//$cmd = $bin_tv." ".$shellpath." -o ".$tfile."[Q=90,optimize_coding] --size=".$p['thumb_size'];
|
|
$cmd = "sips -s format jpeg -s formatOptions 80 --resampleHeightWidthMax ".$p['thumb_size']." ".$shellpath." --out ".$tfile;
|
|
$stmt->BindValue(":tool","sips");
|
|
} elseif (in_array($ext, $t_files['ffmpeg'])) {
|
|
//$cmd = $bin_tf." -i ".$shellpath." -o ".$tfile." -s ".$p['thumb_size']." -c jpg -q 8.5";
|
|
$cmd = $bin_ffmpeg." -ss $(( $(".$bin_mediainfo." --Inform='Video;%Duration%' ".$shellpath.") / 10000 )) -i ".$shellpath." -vframes 1 -filter:v scale='400:-1' -q:v 3 ".$tfile;
|
|
$stmt->BindValue(":tool","ffmpeg");
|
|
}
|
|
|
|
if ($cmd) { shell_exec($cmd." 2>&1"); }
|
|
|
|
// if those tools failed, try quicklook
|
|
if (!@filesize($tfile)) {
|
|
//$cmd = $bin_qlthumb." ".$shellpath." ".$tfile." public.jpeg-2000 ".$p['thumb_size']." ".$p['thumb_size']." .8";
|
|
$cmd = $bin_qlthumb." ".$shellpath." ".$tfile." public.jpeg ".$p['thumb_size']." ".$p['thumb_size']." .8";
|
|
shell_exec($cmd." 2>&1");
|
|
$stmt->BindValue(":tool","quicklook");
|
|
}
|
|
|
|
// success, move thumb into the bundle
|
|
if (file_exists($tfile) && @filesize($tfile)) {
|
|
$stmt->BindValue(":relative_path",substr($tfile, strlen($bpath)));
|
|
list($width, $height) = getimagesize($tfile);
|
|
$stmt->BindValue(":width",$width);
|
|
$stmt->BindValue(":height",$height);
|
|
}
|
|
|
|
$stmt->execute();
|
|
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
}
|
|
|
|
// External metadata
|
|
//////////////////////////////////////////
|
|
|
|
if ($p['meta']) {
|
|
|
|
echo ProgressBar::start(count($fx),"Collecting external metadata");
|
|
|
|
foreach ($fx as $array) {
|
|
|
|
$fid = $array[0];
|
|
$pathname = $array[1];
|
|
$shellpath = escapeshellarg($pathname);
|
|
$ext = pathinfo($pathname,PATHINFO_EXTENSION);
|
|
|
|
if (!in_array($ext, $e_files) && !in_array($ext, $m_files)) {
|
|
echo ProgressBar::next("Not a media file: ".shortlabel($pathname));
|
|
continue;
|
|
} else {
|
|
echo ProgressBar::next("Metadata: ".shortlabel($pathname));
|
|
}
|
|
|
|
if (in_array($ext, $e_files)) {
|
|
$check = $dbp->query("SELECT EXISTS(SELECT 1 FROM exiftool WHERE fid='".$fid."')")->fetch()[0];
|
|
if (!$check) {
|
|
$rawexif = eval("return ".`$bin_exiftool -php $shellpath`);
|
|
$stmt = $dbp->prepare("INSERT INTO exiftool VALUES (:fid, :tags)");
|
|
$stmt->BindValue(":fid",$fid);
|
|
$stmt->BindValue(":tags",serialize($rawexif[0]));
|
|
$stmt->execute();
|
|
}
|
|
}
|
|
|
|
if (in_array($ext, $m_files)) {
|
|
$check = $dbp->query("SELECT EXISTS(SELECT 1 FROM mediainfo WHERE fid='".$fid."')")->fetch()[0];
|
|
if (!$check) {
|
|
$stmt = $dbp->prepare("INSERT INTO mediainfo VALUES (:fid, :info)");
|
|
$stmt->BindValue(":fid",$fid);
|
|
$stmt->BindValue(":info",serialize(parseMediaInfo(shell_exec($bin_mediainfo." --Output=OLDXML ".$shellpath." 2>&1"))));
|
|
$stmt->execute();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
}
|
|
|
|
// Hashes
|
|
//////////////////////////////////////////
|
|
|
|
if ($p['hash']) {
|
|
|
|
if ($p['hash_limit']) {
|
|
$message = "Generating hashes for files under ".$p['hash_limit']."GB";
|
|
} else {
|
|
$message = "Generating hashes for all files";
|
|
}
|
|
|
|
echo ProgressBar::start(count($fx),$message);
|
|
|
|
foreach ($fx as $array) {
|
|
$fid = $array[0];
|
|
$pathname = $array[1];
|
|
$size = filesize($pathname);
|
|
$limit = $p['hash_limit']*1000000000;
|
|
$check = $dbp->query("SELECT EXISTS(SELECT 1 FROM md5 WHERE fid='".$fid."')")->fetch()[0];
|
|
if ($check) {
|
|
echo ProgressBar::next("Hash already exists: ".shortlabel($pathname));
|
|
} elseif ($p['hash_limit'] && ($size > $limit)) {
|
|
echo ProgressBar::next("Too big to hash: ".shortlabel($pathname)." (".human_filesize($size).")");
|
|
} else {
|
|
echo ProgressBar::next("Generating hash: ".shortlabel($pathname));
|
|
$stmt = $dbp->prepare("INSERT INTO md5 VALUES (:fid, :hash)");
|
|
$stmt->BindValue(":fid",$fid);
|
|
$stmt->BindValue(":hash",md5_file($pathname));
|
|
$stmt->execute();
|
|
}
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
}
|
|
|
|
// Files
|
|
//////////////////////////////////////////
|
|
|
|
echo ProgressBar::start($passed_total,"Processing files");
|
|
|
|
foreach ($files as $splFileInfo) {
|
|
|
|
echo "\n";
|
|
|
|
// DB
|
|
|
|
$stmt = $dbo->prepare("INSERT INTO files VALUES (:pid, :fid, :Pathname, :Path, :Filename, :Extension, :Type, :Inode, :Perms, :Owner, :ATime, :CTime, :MTime, :LinkTarget, :RealPath, :stat, :items, :newest, :gfi_type, :gfi_attr, :gfi_created, :Size, :Title, :PixelWidth, :PixelHeight, :Duration, :DateTimeOriginal, :Origin, :GPS, :Author, :spotlight, :kMDItemDateAdded, :kMDItemLastUsedDate, :kMDItemUseCount, :kMDItemContentModificationDate, :kMDItemContentType, :kMDItemCreator, :kMDItemFSCreatorCode, :kMDItemKind, :kMDItemFSTypeCode, :kMDItemUserTags, :kMDItemFSInvisible, :kMDItemNumberOfPages, :kMDItemPageHeight, :kMDItemPageWidth, :kMDItemWhereFroms, :kMDItemEncodingApplications, :has_exif, :has_mediainfo, :has_hash, :thumb_filename, :thumb_width, :thumb_height, :ProfileDescription, :BitDepth, :Compression, :Orientation, :LensType, :VideoFormat, :AudioFormat, :Tracks, :Profile, :Bitrate)");
|
|
|
|
// Identify dir, file, link or bundle dir
|
|
|
|
$type = $splFileInfo->getType();
|
|
if ($type == "dir") {
|
|
foreach ($p['bundles'] as $bundle) {
|
|
$check = ".".$bundle;
|
|
if (substr($splFileInfo->getFilename(), -(strlen($check)), strlen($check)) == $check) { $type = "bundle"; }
|
|
}
|
|
}
|
|
|
|
$stmt->BindValue(":Type",$type);
|
|
|
|
// Cache atime before it gets modified
|
|
|
|
if ($type != "link") { $atime = $splFileInfo->getATime(); }
|
|
|
|
// Path basics
|
|
|
|
$pathname = $splFileInfo->getPathname();
|
|
$path = $splFileInfo->getPath();
|
|
$filename = $splFileInfo->getFilename();
|
|
$extension = $splFileInfo->getExtension();
|
|
$shellpath = escapeshellarg($pathname);
|
|
|
|
$stmt->BindValue(":Pathname",$pathname);
|
|
$stmt->BindValue(":Path",$path);
|
|
$stmt->BindValue(":Filename",$filename);
|
|
$stmt->BindValue(":Extension",$extension);
|
|
|
|
if ($type == "link") {
|
|
|
|
$stmt->BindValue(":LinkTarget",$splFileInfo->getLinkTarget());
|
|
$stmt->BindValue(":RealPath",$splFileInfo->getRealPath());
|
|
|
|
} else {
|
|
|
|
$stmt->BindValue(":Inode",$splFileInfo->getInode());
|
|
$stmt->BindValue(":Perms",$splFileInfo->getPerms());
|
|
$stmt->BindValue(":Owner",$splFileInfo->getOwner().":".$splFileInfo->getGroup());
|
|
|
|
$stmt->BindValue(":ATime",$atime);
|
|
$stmt->BindValue(":CTime",$splFileInfo->getCTime());
|
|
$stmt->BindValue(":MTime",$splFileInfo->getMTime());
|
|
|
|
}
|
|
|
|
echo shortlabel($pathname,50,1);
|
|
|
|
// ------------------------------------------------ //
|
|
|
|
// Get stat
|
|
|
|
if ($type != "link") {
|
|
$stat = chop(@shell_exec("stat -x ".$shellpath." 2>&1"));
|
|
} else {
|
|
$stat = null;
|
|
}
|
|
$stmt->BindValue(":stat",@$stat);
|
|
|
|
// Cache stat
|
|
|
|
if ($type != "link" && $p['paranoid']) {
|
|
$pre_access = null;
|
|
$pre_modify = null;
|
|
$pre_change = null;
|
|
foreach (explode("\n", $stat) as $line) {
|
|
$check = substr($line, 0, 6);
|
|
if ($check == "Access") { $pre_access = $line; }
|
|
if ($check == "Modify") { $pre_modify = $line; }
|
|
if ($check == "Change") { $pre_change = $line; }
|
|
}
|
|
}
|
|
|
|
// Generate PID and FID
|
|
|
|
$pid = md5($pathname);
|
|
$stmt->BindValue(":pid",$pid);
|
|
|
|
if ($type == "file") {
|
|
$fid = md5($splFileInfo->getSize().$splFileInfo->getMtime().$splFileInfo->getBasename());
|
|
$stmt->BindValue(":fid",$fid);
|
|
} else {
|
|
$stmt->BindValue(":fid",null);
|
|
}
|
|
|
|
// Size
|
|
|
|
if ($type == "dir" || $type == "bundle") {
|
|
$size = trim(shell_exec("du -ks ".$shellpath." | cut -f1"))*1024;
|
|
} elseif ($type == "file") {
|
|
$size = $splFileInfo->getSize();
|
|
} else {
|
|
$size = null;
|
|
}
|
|
$stmt->BindValue(":Size",@$size);
|
|
stringPrint(floor($size/1024)."k");
|
|
|
|
// ------------------------------------------------ //
|
|
|
|
// Items
|
|
|
|
if ($type == "dir" || $type == "bundle" ) {
|
|
$items = chop(@shell_exec("find ".$shellpath." \( ! -regex '.*/\..*' \) | wc -l 2>&1"))-1;
|
|
$stmt->BindValue(":items",@$items);
|
|
stringPrint($items ? "ITEMS" : "items");
|
|
} else {
|
|
$items = null;
|
|
stringPrint(" ");
|
|
}
|
|
|
|
// ------------------------------------------------ //
|
|
|
|
// Newest
|
|
|
|
if ($type == "dir") {
|
|
$newest = @filemtime(chop(shell_exec("find ".$shellpath." -type f -not -path '*/\.*' -print0 | xargs -0 stat -f \"%m %N\" | sort -rn 2>&1 | head -1 | cut -f2- -d\" \"")));
|
|
$stmt->BindValue(":newest",@$newest);
|
|
stringPrint($newest ? "NEWEST" : "newest");
|
|
} else {
|
|
$newest = null;
|
|
stringPrint(" ");
|
|
}
|
|
|
|
// ------------------------------------------------ //
|
|
|
|
// GetFileInfo
|
|
|
|
$gfiparts = explode("\n", chop(shell_exec($bin_gfi." -P ".$shellpath." 2>&1")));
|
|
if (is_array($gfiparts)) {
|
|
foreach ($gfiparts as $line) {
|
|
list($label, $value) = explode(": ", $line);
|
|
$gfi[$label] = isset($value) ? trim($value,"\"") : null;
|
|
}
|
|
}
|
|
|
|
$writegfitype = @$gfi['type'].":".@$gfi['creator'];
|
|
if ($writegfitype == "\\0\\0\\0\\0:\\0\\0\\0\\0" || $writegfitype == ":") { $writegfitype = null; }
|
|
|
|
$stmt->BindValue(":gfi_type",$writegfitype);
|
|
$stmt->BindValue(":gfi_attr",@$gfi['attributes']);
|
|
$stmt->BindValue(":gfi_created",strtotime($gfi['created']));
|
|
|
|
stringPrint("GFI");
|
|
// ------------------------------------------------ //
|
|
|
|
// Spotlight
|
|
|
|
$mdls = null;
|
|
$mdls = shell_exec("mdls -plist - ".$shellpath." 2>&1");
|
|
|
|
if ($mdls != $pathname.": could not find ".$pathname.".\n") {
|
|
$parser = new plistParser();
|
|
$spotlight = $parser->parseString($mdls);
|
|
//$stmt->BindValue(":spotlight",serialize($spotlight));
|
|
$stmt->BindValue(":spotlight",$mdls);
|
|
} else {
|
|
$spotlight = array();
|
|
$stmt->BindValue(":spotlight",null);
|
|
}
|
|
|
|
stringPrint($mdls ? "MDLS" : "mdls");
|
|
|
|
unset($breakout, $schema, $item, $ready);
|
|
|
|
$breakout[] = array ("kMDItemDateAdded", "date");
|
|
$breakout[] = array ("kMDItemLastUsedDate", "date");
|
|
$breakout[] = array ("kMDItemUseCount", 0);
|
|
$breakout[] = array ("kMDItemContentModificationDate", "date");
|
|
$breakout[] = array ("kMDItemContentType", 0);
|
|
$breakout[] = array ("kMDItemCreator", 0);
|
|
$breakout[] = array ("kMDItemFSCreatorCode", 0);
|
|
$breakout[] = array ("kMDItemKind", 0);
|
|
$breakout[] = array ("kMDItemFSTypeCode", 0);
|
|
$breakout[] = array ("kMDItemUserTags", "array");
|
|
$breakout[] = array ("kMDItemFSInvisible", 0);
|
|
$breakout[] = array ("kMDItemNumberOfPages", 0);
|
|
$breakout[] = array ("kMDItemPageHeight", 0);
|
|
$breakout[] = array ("kMDItemPageWidth", 0);
|
|
$breakout[] = array ("kMDItemWhereFroms", "array");
|
|
$breakout[] = array ("kMDItemEncodingApplications", "array");
|
|
|
|
foreach ($breakout as $schema) {
|
|
if (!isset($spotlight[$schema[0]])) {
|
|
$stmt->BindValue(":".$schema[0],null);
|
|
continue;
|
|
}
|
|
if ($schema[1] === "date") {
|
|
$ready = strtotime($spotlight[$schema[0]]);
|
|
} elseif ($schema[1] === "array") {
|
|
$ready = serialize($spotlight[$schema[0]]);
|
|
} else {
|
|
$ready = $spotlight[$schema[0]];
|
|
}
|
|
$stmt->BindValue(":".$schema[0],$ready);
|
|
}
|
|
|
|
unset($breakout);
|
|
|
|
// ------------------------------------------------ //
|
|
|
|
// Pool
|
|
|
|
if ($type == "file") {
|
|
|
|
unset($fetch_exif, $fetch_media, $fetch_hash, $fetch_thumb, $yes_exif, $yes_media, $yes_hash);
|
|
|
|
$fetch_exif = @unserialize($dbp->query("SELECT tags FROM exiftool WHERE fid='".$fid."'")->fetch()[0]);
|
|
is_array($fetch_exif) ? $yes_exif = 1 : $yes_exif = 0;
|
|
$stmt->BindValue(":has_exif",$yes_exif);
|
|
stringPrint($yes_exif ? "EXIF" : "exif");
|
|
|
|
$fetch_media = @unserialize($dbp->query("SELECT info FROM mediainfo WHERE fid='".$fid."'")->fetch()[0]);
|
|
is_array($fetch_media) ? $yes_media = 1 : $yes_media = 0;
|
|
$stmt->BindValue(":has_mediainfo",$yes_media);
|
|
stringPrint($yes_media ? "MEDIA" : "media");
|
|
|
|
$yes_hash = $dbp->query("SELECT EXISTS(SELECT 1 FROM md5 WHERE fid='".$fid."')")->fetch()[0];
|
|
$stmt->BindValue(":has_hash",$yes_hash);
|
|
stringPrint($yes_hash ? "HASH" : "hash");
|
|
|
|
$fetch_thumb = $dbp->query("SELECT * FROM thumbs WHERE fid='".$fid."'")->fetch();
|
|
if (@$fetch_thumb['relative_path']) {
|
|
$stmt->BindValue(":thumb_filename",$fetch_thumb['relative_path']);
|
|
$stmt->BindValue(":thumb_width",$fetch_thumb['width']);
|
|
$stmt->BindValue(":thumb_height",$fetch_thumb['height']);
|
|
stringPrint("THUMB");
|
|
} else {
|
|
$stmt->BindValue(":thumb_filename",null);
|
|
stringPrint("thumb");
|
|
}
|
|
|
|
$breakout[] = "ProfileDescription";
|
|
$breakout[] = "BitDepth_BitsPerSample";
|
|
$breakout[] = "Compression";
|
|
$breakout[] = "Aperture,LightSource,WhiteBalance";
|
|
$breakout[] = "Orientation";
|
|
$breakout[] = "LensType,FocalLength";
|
|
|
|
$breakout['profile'] = "ProfileDescription";
|
|
$breakout['bits'] = "BitDepth_BitsPerSample";
|
|
$breakout['compression'] = "Compression";
|
|
$breakout[] = "Aperture,LightSource,WhiteBalance";
|
|
$breakout[] = "Orientation";
|
|
$breakout[] = "LensType,FocalLength";
|
|
|
|
/*
|
|
|
|
function parseConditionalItem($data, $item) {
|
|
if (strpos($item, "_")) {
|
|
$list = explode("_", $item);
|
|
foreach ($list as $piece) {
|
|
|
|
// left off here
|
|
|
|
}
|
|
}
|
|
|
|
|
|
function parseItem($data, $item) {
|
|
if (strpos($item, ",")) {
|
|
$list = explode(",", $item);
|
|
foreach ($list as $piece) {
|
|
$cleared[] = parseConditionalItem($piece);
|
|
}
|
|
} else {
|
|
$cleared[] = parseConditionalItem($item);
|
|
}
|
|
foreach ($cleared as $check) {
|
|
if (isset($data[$check])) {
|
|
$ready[] = $data[$check];
|
|
}
|
|
}
|
|
if (@count($ready) > 1) {
|
|
return implode(", ",$ready);
|
|
} elseif (@count($ready) == 1) {
|
|
return $ready[0];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// left off here
|
|
|
|
foreach ($breakout as $item) {
|
|
unset($ready);
|
|
if (strpos($item, ",")) {
|
|
$list = explode(",", $item) {
|
|
foreach ($list as $multi) {
|
|
if (isset($fetch_exif[$multi])) {
|
|
$ready[] = $fetch_exif[$multi];
|
|
}
|
|
}
|
|
} elseif (strpos($item, "_")) {
|
|
$list = explode("_", $item) {
|
|
foreach ($list as $multi) {
|
|
if (isset($ready) {
|
|
continue;
|
|
}
|
|
if (isset($fetch_exif[$multi])) {
|
|
$ready[] = $fetch_exif[$multi];
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
unset($breakout);
|
|
|
|
//print_r($fetch_exif);
|
|
//print_r($fetch_media);
|
|
|
|
}
|
|
|
|
// Write to DB
|
|
|
|
$stmt->execute();
|
|
stringPrint("DB");
|
|
|
|
// Set fileatime back to original value
|
|
|
|
if ($type != "link" && is_writable($pathname) && $p['fixatimes']) {
|
|
@exec("touch -at `date -r ".$atime." +%Y%m%d%H%M.%S` ".$shellpath." 2>&1");
|
|
stringPrint("touch");
|
|
}
|
|
|
|
// Double check stat for file against pre-run value
|
|
|
|
if ($type != "link" && $p['paranoid']) {
|
|
|
|
$restat = chop(@shell_exec("stat -x ".$shellpath." 2>&1"));
|
|
$post_access = null;
|
|
$post_modify = null;
|
|
$post_change = null;
|
|
|
|
foreach (explode("\n", $restat) as $line) {
|
|
$check = substr($line, 0, 6);
|
|
if ($check == "Access") { $post_access = $line; }
|
|
if ($check == "Modify") { $post_modify = $line; }
|
|
if ($check == "Change") { $post_change = $line; }
|
|
}
|
|
|
|
$message = array();
|
|
if ($pre_access != $post_access) {
|
|
$message[] = "ATIME";
|
|
}
|
|
if ($pre_modify != $post_modify) {
|
|
$message[] = "MTIME";
|
|
}
|
|
if ($pre_change != $post_change) {
|
|
$message[] = "CTIME";
|
|
}
|
|
|
|
if (count($message)) { stringPrint("Changed: ".implode(", ", $message)); }
|
|
|
|
}
|
|
|
|
echo "\n";
|
|
echo ProgressBar::next();
|
|
|
|
}
|
|
|
|
echo ProgressBar::finish();
|
|
|
|
// 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";
|
|
|
|
unset($dbo, $dbp, $files, $family, $fx);
|
|
|
|
?>
|