1 | <?php |
||
41 | class ScanStoragesCommand extends ContainerAwareCommand |
||
42 | { |
||
43 | /** |
||
44 | * Allowable extension |
||
45 | * |
||
46 | * @var array |
||
47 | */ |
||
48 | protected $allow_ext = [ |
||
49 | 'avi', |
||
50 | 'mkv', |
||
51 | 'm1v', |
||
52 | 'm2v', |
||
53 | 'm4v', |
||
54 | 'mov', |
||
55 | 'qt', |
||
56 | 'mpeg', |
||
57 | 'mpg', |
||
58 | 'mpe', |
||
59 | 'ogg', |
||
60 | 'rm', |
||
61 | 'wmv', |
||
62 | 'asf', |
||
63 | 'wm', |
||
64 | 'm2ts', |
||
65 | 'mts', |
||
66 | 'm2t', |
||
67 | 'mp4', |
||
68 | 'mov', |
||
69 | '3gp', |
||
70 | '3g2', |
||
71 | 'k3g', |
||
72 | 'mp2', |
||
73 | 'mpv2', |
||
74 | 'mod', |
||
75 | 'vob', |
||
76 | 'f4v', |
||
77 | 'ismv' |
||
78 | ]; |
||
79 | |||
80 | protected function configure() |
||
81 | { |
||
82 | $this |
||
83 | ->setName('animedb:scan-storage') |
||
84 | ->setDescription('Scan storages for new items') |
||
85 | ->addArgument( |
||
86 | 'storage', |
||
87 | InputArgument::OPTIONAL, |
||
88 | 'Id scanned storage' |
||
89 | ) |
||
90 | ->addOption( |
||
91 | 'force', |
||
92 | 'f', |
||
93 | InputOption::VALUE_NONE, |
||
94 | 'Ignore the last modified storage' |
||
95 | ) |
||
96 | ->addOption( |
||
97 | 'no-progress', |
||
98 | null, |
||
99 | InputOption::VALUE_NONE, |
||
100 | 'Disable progress bar' |
||
101 | ) |
||
102 | ->addOption( |
||
103 | 'export', |
||
104 | null, |
||
105 | InputOption::VALUE_REQUIRED, |
||
106 | 'Export progress to file (disables progress as --no-progress)' |
||
107 | ) |
||
108 | ->setHelp(<<<EOT |
||
109 | Example scan all storages: |
||
110 | |||
111 | <info>php app/console animedb:scan-storage</info> |
||
112 | |||
113 | Example scan storage with id <info>1</info>: |
||
114 | |||
115 | <info>php app/console animedb:scan-storage 1</info> |
||
116 | EOT |
||
117 | ); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @param InputInterface $input |
||
122 | * @param OutputInterface $output |
||
123 | * |
||
124 | * @return int |
||
125 | */ |
||
126 | protected function execute(InputInterface $input, OutputInterface $output) { |
||
127 | $start = time(); |
||
128 | |||
129 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); |
||
130 | $dispatcher = $this->getContainer()->get('event_dispatcher'); |
||
131 | /* @var $rep StorageRepository */ |
||
132 | $rep = $em->getRepository('AnimeDbCatalogBundle:Storage'); |
||
133 | |||
134 | $progress = $this->getProgress($input, $output); |
||
135 | $lazywrite = new LazyWrite($output); |
||
136 | $lazywrite->setLazyWrite(!$input->getOption('no-progress')); |
||
137 | |||
138 | // get list storages |
||
139 | if ($id = $input->getArgument('storage')) { |
||
140 | $storage = $rep->find($id); |
||
141 | if (!($storage instanceof Storage)) { |
||
142 | throw new \InvalidArgumentException('Not found the storage with id: '.$id); |
||
143 | } |
||
144 | if (!$storage->isWritable()) { |
||
145 | throw new \InvalidArgumentException('Storage "'.$storage->getName().'" can not be scanned'); |
||
146 | } |
||
147 | $storages = [$storage]; |
||
148 | } else { |
||
149 | $storages = $rep->getList(Storage::getTypesWritable()); |
||
150 | } |
||
151 | |||
152 | /* @var $storage Storage */ |
||
153 | foreach ($storages as $storage) { |
||
154 | $output->writeln('Scan storage <info>'.$storage->getName().'</info>:'); |
||
155 | |||
156 | $path = $storage->getPath(); |
||
157 | $path = Utf8::wrapPath($path); // wrap path for current fs |
||
158 | |||
159 | if (!file_exists($path)) { |
||
160 | $output->writeln('Storage is not available'); |
||
161 | continue; |
||
162 | } |
||
163 | |||
164 | // check storage id |
||
165 | $owner = $this->checkStorageId($path, $storage, $rep); |
||
166 | if ($owner instanceof Storage) { |
||
167 | $output->writeln('Path <info>'.$storage->getPath().'</info> reserved storage <info>' |
||
168 | .$owner->getName().'</info>'); |
||
169 | continue; |
||
170 | } |
||
171 | |||
172 | // storage not modified |
||
173 | if (!$input->getOption('force') && |
||
174 | $storage->getFileModified() && |
||
175 | filemtime($path) == $storage->getFileModified()->getTimestamp() |
||
176 | ) { |
||
177 | $output->writeln('Storage is not modified'); |
||
178 | continue; |
||
179 | } |
||
180 | |||
181 | $files = $this->getFilesByPath($path); |
||
182 | $total = $files->count(); |
||
183 | // total files +1% for check of delete files |
||
184 | $progress->start(ceil($total+($total*0.01))); |
||
185 | $progress->display(); |
||
186 | |||
187 | /* @var $file SplFileInfo */ |
||
188 | foreach ($files as $file) { |
||
189 | // ignore not supported files |
||
190 | if ($file->isFile() && !$this->isAllowFile($file)) { |
||
191 | $progress->advance(); |
||
192 | continue; |
||
193 | } |
||
194 | |||
195 | // item is exists and modified |
||
196 | if ($item = $this->getItemFromFile($storage, $file)) { |
||
197 | if ($item->getDateUpdate()->getTimestamp() < $file->getPathInfo()->getMTime()) { |
||
198 | $dispatcher->dispatch(StoreEvents::UPDATE_ITEM_FILES, new UpdateItemFiles($item)); |
||
|
|||
199 | $lazywrite->writeln('Changes are detected in files of item <info>'.$item->getName().'</info>'); |
||
200 | } |
||
201 | } else { |
||
202 | // remove wrap prefix |
||
203 | list(, $file) = explode('://', $file->getPathname(), 2); |
||
204 | $file = new SplFileInfo($file, '', ''); |
||
205 | |||
206 | // it is a new item |
||
207 | $dispatcher->dispatch(StoreEvents::DETECTED_NEW_FILES, new DetectedNewFiles($storage, $file)); |
||
208 | $lazywrite->writeln('Detected files for new item <info>'.$file->getFilename().'</info>'); |
||
209 | } |
||
210 | $progress->advance(); |
||
211 | } |
||
212 | $em->refresh($storage); |
||
213 | |||
214 | // check of delete file for item |
||
215 | foreach ($this->getItemsOfDeletedFiles($storage, $files) as $item) { |
||
216 | $dispatcher->dispatch(StoreEvents::DELETE_ITEM_FILES, new DeleteItemFiles($item)); |
||
217 | $lazywrite->writeln('<error>Files for item "'.$item->getName().'" is not found</error>'); |
||
218 | } |
||
219 | $progress->advance(); |
||
220 | $progress->finish(); |
||
221 | $lazywrite->writeAll(); |
||
222 | |||
223 | // update date modified |
||
224 | $storage->setFileModified(new \DateTime(date('Y-m-d H:i:s', filemtime($path)))); |
||
225 | $em->persist($storage); |
||
226 | $output->writeln(''); |
||
227 | } |
||
228 | $em->flush(); |
||
229 | |||
230 | $output->writeln('Time: <info>'.(time()-$start).'</info> s.'); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Get items of deleted files |
||
235 | * |
||
236 | * @param Storage $storage |
||
237 | * @param Finder $finder |
||
238 | * |
||
239 | * @return array |
||
240 | */ |
||
241 | protected function getItemsOfDeletedFiles(Storage $storage, Finder $finder) |
||
255 | |||
256 | /** |
||
257 | * Get item from files |
||
258 | * |
||
259 | * @param Storage $storage |
||
260 | * @param SplFileInfo $file |
||
261 | * |
||
262 | * @return Item|bool |
||
263 | */ |
||
264 | protected function getItemFromFile(Storage $storage, SplFileInfo $file) |
||
265 | { |
||
266 | /* @var $item Item */ |
||
267 | foreach ($storage->getItems() as $item) { |
||
268 | if (pathinfo($item->getPath(), PATHINFO_BASENAME) == $file->getFilename()) { |
||
269 | return $item; |
||
270 | } |
||
271 | } |
||
272 | return false; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Get files by path |
||
277 | * |
||
278 | * @param string $path |
||
279 | * |
||
280 | * @return Finder |
||
281 | */ |
||
282 | protected function getFilesByPath($path) |
||
283 | { |
||
284 | return Finder::create() |
||
285 | ->in($path) |
||
286 | ->ignoreUnreadableDirs() |
||
287 | ->depth('== 0') |
||
288 | ->notName('.*'); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Is allow file |
||
293 | * |
||
294 | * @param SplFileInfo $file |
||
295 | * |
||
296 | * @return bool |
||
297 | */ |
||
298 | protected function isAllowFile(SplFileInfo $file) |
||
302 | |||
303 | /** |
||
304 | * Update storage id |
||
305 | * |
||
306 | * @param string $path |
||
307 | * @param Storage $storage |
||
308 | * @param StorageRepository $rep |
||
309 | * |
||
310 | * @return Storage|bool |
||
311 | */ |
||
312 | protected function checkStorageId($path, Storage $storage, StorageRepository $rep) |
||
328 | |||
329 | /** |
||
330 | * Get progress |
||
331 | * |
||
332 | * @param InputInterface $input |
||
333 | * @param OutputInterface $output |
||
334 | * |
||
335 | * @return Decorator |
||
336 | */ |
||
337 | protected function getProgress(InputInterface $input, OutputInterface $output) |
||
360 | } |
||
361 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.