1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at https://github.com/JamesHeinrich/getID3       //
5//            or https://www.getid3.org                        //
6//            or http://getid3.sourceforge.net                 //
7//                                                             //
8// /demo/demo.dirscan.php - part of getID3()                   //
9// Directory Scanning and Caching CLI tool for batch media     //
10//   file processing with getID3()                             //
11//  by Karl G. Holz <newaeonØmac*com>                          //
12//                                                            ///
13/////////////////////////////////////////////////////////////////
14
15/**
16* This is a directory scanning and caching cli tool for getID3().
17*
18* use like so for the default sqlite3 database, which is hidden:
19*
20* cd <path you want to start scanning from>
21* php <path to getid3 files>/demo.dirscan.php
22*
23* or
24*
25* php <path to getid3 files>/demo.dirscan.php <dir to scan> <file ext in csv list>
26*
27* Supported Cache Types    (this extension)
28*
29*   SQL Databases:
30*
31*   cache_type
32*   -------------------------------------------------------------------
33*    mysql
34
35$cache='mysql';
36$database['host']='';
37$database['database']='';
38$database['username']='';
39$database['password']='';
40$database['table']='';
41
42*    sqlite3
43
44$cache='sqlite3';
45$database['table']='getid3_cache';
46$database['hide']=true;
47
48*/
49$dir      = $_SERVER['PWD'];
50$media    = array('mp4', 'm4v', 'mov', 'mp3', 'm4a', 'jpg', 'png', 'gif');
51$database = array();
52/**
53* configure the database bellow
54*/
55// sqlite3
56$cache             = 'sqlite3';
57$database['table'] = 'getid3_cache';
58$database['hide']  = true;
59/**
60 * mysql
61$cache                = 'mysql';
62$database['host']     = '';
63$database['database'] = '';
64$database['username'] = '';
65$database['password'] = '';
66$database['table']    = '';
67*/
68
69/**
70* id3 tags class file
71*/
72require_once(dirname(__FILE__).'/getid3.php');
73/**
74* dirscan scans all directories for files that match your selected filetypes into the cache database
75* this is useful for a lot of media files
76*
77*
78* @package dirscan
79* @author Karl Holz
80*
81*/
82
83class dirscan {
84	/**
85	* type_brace()  * Might not work on Solaris and other non GNU systems *
86	*
87	* Configures a filetype list for use with glob searches,
88	* will match uppercase or lowercase extensions only, no mixing
89	* @param string $dir directory to use
90	* @param mixed $search cvs list of extentions or an array
91	* @return string or null if checks fail
92	*/
93	private function type_brace($dir, $search=array()) {
94		$dir = str_replace(array('///', '//'), array('/', '/'), $dir);
95		if (!is_dir($dir)) {
96			return null;
97		}
98		if (!is_array($search)) {
99			$e = explode(',', $search);
100		} elseif (count($search) < 1) {
101			return null;
102		} else {
103			$e = $search;
104		}
105		$ext = array();
106		foreach ($e as $new) {
107			$ext[] = strtolower(trim($new));
108			$ext[] = strtoupper(trim($new));
109		}
110		$b = $dir.'/*.{'.implode(',', $ext).'}';
111		return $b;
112	}
113
114	/**
115	* this function will search 4 levels deep for directories
116	* will return null on failure
117	* @param string $root
118	* @return array return an array of dirs under root
119	* @todo figure out how to block tabo directories with ease
120	*/
121	private function getDirs($root) {
122		switch ($root) { // return null on tabo directories, add as needed -> case {dir to block }:   this is not perfect yet
123			case '/':
124			case '/var':
125			case '/etc':
126			case '/home':
127			case '/usr':
128			case '/root':
129			case '/private/etc':
130			case '/private/var':
131			case '/etc/apache2':
132			case '/home':
133			case '/tmp':
134			case '/var/log':
135				return null;
136				break;
137			default: // scan 4 directories deep
138				if (!is_dir($root)) {
139    				return null;
140				}
141				$dirs = array_merge(glob($root.'/*', GLOB_ONLYDIR), glob($root.'/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*', GLOB_ONLYDIR), glob($root.'/*/*/*/*/*/*/*', GLOB_ONLYDIR));
142				break;
143		}
144		if (count($dirs) < 1) {
145			$dirs = array($root);
146		}
147		return $dirs;
148	}
149
150	/**
151	*  file_check() check the number of file that are found that match the brace search
152	*
153	* @param string $search
154	* @return mixed
155	*/
156	private function file_check($search) {
157		$t = array();
158		$s = glob($search, GLOB_BRACE);
159		foreach ($s as $file) {
160			$t[] = str_replace(array('///', '//'), array('/', '/'), $file);
161		}
162		if (count($t) > 0) {
163			return $t;
164		}
165		return null;
166	}
167
168	function getTime() {
169		return microtime(true);
170		// old method for PHP < 5
171		//$a = explode(' ', microtime());
172		//return (double) $a[0] + $a[1];
173	}
174
175
176	/**
177	*
178	* @param string $dir
179	* @param mixed  $match  search type name extentions, can be an array or csv list
180	* @param string $cache caching extention, select one of sqlite3, mysql, dbm
181	* @param array  $opt database options,
182	*/
183	function scan_files($dir, $match, $cache='sqlite3', $opt=array('table'=>'getid3_cache', 'hide'=>true)) {
184		$Start = self::getTime();
185		switch ($cache) { // load the caching module
186			case 'sqlite3':
187				if (!class_exists('getID3_cached_sqlite3')) {
188					require_once(dirname(__FILE__)).'/extension.cache.sqlite3.php';
189				}
190				$id3 = new getID3_cached_sqlite3($opt['table'], $opt['hide']);
191				break;
192			case 'mysql':
193				if (!class_exists('getID3_cached_mysql')) {
194					require_once(dirname(__FILE__)).'/extension.cache.mysql.php';
195				}
196				$id3 = new getID3_cached_mysql($opt['host'], $opt['database'], $opt['username'], $opt['password'], $opt['table']);
197				break;
198		// I'll leave this for some one else
199			//case 'dbm':
200			//	if (!class_exists('getID3_cached_dbm')) {
201			//		require_once(dirname(__FILE__)).'/extension.cache.dbm.php';
202			//	}
203			//	die(' This has not be implemented, sorry for the inconvenience');
204			//	break;
205			default:
206				die(' You have selected an Invalid cache type, only "sqlite3" and "mysql" are valid'."\n");
207				break;
208		}
209		$count = array('dir'=>0, 'file'=>0);
210		$dirs = self::getDirs($dir);
211		if ($dirs !== null) {
212			foreach ($dirs as $d) {
213				echo ' Scanning: '.$d."\n";
214				$search = self::type_brace($d, $match);
215				if ($search !== null) {
216    				$files = self::file_check($search);
217					if ($files !== null) {
218						foreach ($files as $f) {
219							echo ' * Analyzing '.$f.' '."\n";
220							$id3->analyze($f);
221							$count['file']++;
222						}
223						$count['dir']++;
224					} else {
225						echo 'Failed to get files '."\n";
226					}
227				} else {
228					echo 'Failed to create match string '."\n";
229				}
230			}
231			echo '**************************************'."\n";
232			echo '* Finished Scanning your directories '."\n*\n";
233			echo '* Directories '.$count['dir']."\n";
234			echo '* Files '.$count['file']."\n";
235			$End = self::getTime();
236			$t = number_format(($End - $Start) / 60, 2);
237			echo '* Time taken to scan '.$dir.' '.$t.' min '."\n";
238			echo '**************************************'."\n";
239		} else {
240			echo ' failed to get directories '."\n";
241		}
242	}
243}
244
245if (PHP_SAPI === 'cli') {
246	if (count($argv) == 2) {
247		if (is_dir($argv[1])) {
248			$dir = $argv[1];
249		}
250		if (count(explode(',', $argv[2])) > 0) {
251			$media = $arg[2];
252		}
253	}
254	echo ' * Starting to scan directory: '.$dir."\n";
255	echo ' * Using default media types: '.implode(',', $media)."\n";
256	dirscan::scan_files($dir, $media, $cache, $database);
257}
258