output->error('The image processing queue is not enabled'); } $rabbitmq = new RabbitMQService(); $this->output->writeln('Monitoring queue'); $rabbitmq->waitOnQueue([$this, 'processQueueItem']); } /** * Processes a single item from the queue. * * @param $msg * @return void */ public function processQueueItem($msg) { $queueItemID = intval($msg->body); $this->output->writeln(sprintf('Processing queue item %d', $queueItemID)); /** @var QueueItem $queueItem */ $queueItem = QueueItem::where('id', $queueItemID)->first(); if (is_null($queueItem)) { $this->output->writeln('Queue item does not exist; skipping'); $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); return; } try { switch (strtolower($queueItem->action_type)) { case 'photo.analyse': $this->processPhotoAnalyseMessage($queueItem); break; case 'photo.bulk_action.change_album': case 'photo.bulk_action.delete': case 'photo.bulk_action.flip_both': case 'photo.bulk_action.flip_horizontal': case 'photo.bulk_action.flip_vertical': case 'photo.bulk_action.refresh_thumbnails': case 'photo.bulk_action.rotate_left': case 'photo.bulk_action.rotate_right': $this->processPhotoBulkActionMessage($queueItem); break; default: $this->output->writeln(sprintf('Action %s is not recognised, skipping', $queueItem->action_type)); return; } $queueItem->completed_at = new \DateTime(); $queueItem->save(); } catch (\Exception $ex) { $this->output->error($ex->getMessage()); $queueItem->error_message = $ex->getMessage(); } finally { $queueItem->completed_at = new \DateTime(); $queueItem->save(); $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); } } private function createActivityRecord(Photo $photo, $type, $userID, $activityDateTime = null) { if (is_null($activityDateTime)) { $activityDateTime = new \DateTime(); } $userActivity = new UserActivity(); $userActivity->user_id = $userID; $userActivity->activity_at = $activityDateTime; $userActivity->type = $type; $userActivity->photo_id = $photo->id; $userActivity->save(); } private function processPhotoAnalyseMessage(QueueItem $queueItem) { $this->output->writeln(sprintf('Analysing photo ID %l (batch: %s)', $queueItem->photo_id, $queueItem->batch_reference)); /** @var Photo $photo */ $photo = $queueItem->photo; if (is_null($photo)) { $this->output->writeln('Photo does not exist; skipping'); return; } /* IF CHANGING THIS LOGIC, ALSO CHECK PhotoController::analyse */ $photoService = new PhotoService($photo); $photoService->analyse($queueItem->batch_reference); // Log an activity record for the user's feed (remove an existing one as the date may have changed) $this->removeExistingActivityRecords($photo, 'photo.taken'); if (!is_null($photo->taken_at)) { // Log an activity record for the user's feed $this->createActivityRecord($photo, 'photo.taken', $queueItem->user_id, $photo->taken_at); } $queueItem->is_successful = true; } private function processPhotoBulkActionMessage(QueueItem $queueItem) { $action = str_replace('photo.bulk_action.', '', $queueItem->action_type); $this->output->writeln(sprintf('Apply action \'%s\' to photo ID %l (batch: %s)', $action, $queueItem->photo_id, $queueItem->batch_reference)); /** @var Photo $photo */ $photo = $queueItem->photo; if (is_null($photo)) { $this->output->writeln('Photo does not exist; skipping'); return; } /** @var User $user */ $user = $queueItem->user; if (is_null($user)) { $this->output->writeln('User does not exist; skipping'); return; } $photoService = new PhotoService($photo); switch (strtolower($action)) { /* This needs a bit more work - we need to also store the new album ID in the queue_items table */ case 'change_album': if ($user->can('change-metadata', $photo)) { $newAlbum = Album::where('id', intval($queueItem->new_album_id))->first(); if (is_null($newAlbum) || !$user->can('upload-photos', $newAlbum)) { $this->output->writeln('Target album does not exist or user does not have permission.'); return; } $this->output->writeln(sprintf('Moving photo to album \'%s\'', $newAlbum->name)); $photoService->changeAlbum($newAlbum); } break; case 'delete': if ($user->can('delete', $photo)) { $photoService->delete(); } break; case 'flip_both': if ($user->can('manipulate', $photo)) { $photoService->flip(true, true); } break; case 'flip_horizontal': if ($user->can('manipulate', $photo)) { $photoService->flip(true, false); } break; case 'flip_vertical': if ($user->can('manipulate', $photo)) { $photoService->flip(false, true); } break; case 'refresh_thumbnails': if ($user->can('change-metadata', $photo)) { $photoService->regenerateThumbnails(); } break; case 'rotate_left': if ($user->can('manipulate', $photo)) { $photoService->rotate(90); } break; case 'rotate_right': if ($user->can('manipulate', $photo)) { $photoService->rotate(270); } break; default: $this->output->writeln(sprintf('Action \'%s\' not recognised; skipping')); return; } } private function removeExistingActivityRecords(Photo $photo, $type) { $existingFeedRecords = UserActivity::where([ 'user_id' => $photo->user_id, 'photo_id' => $photo->id, 'type' => $type ])->get(); foreach ($existingFeedRecords as $existingFeedRecord) { $existingFeedRecord->delete(); } } }