In Auctibles, we use ApyHub to moderate images of items for auction. Auctibles is built using the Laravel PHP framework.
Uploading Image Files
Videos are effortlessly uploaded using a straightforward Livewire component and an HTML input
element with type=file
.
<input id="photos" name="photos" type="file" class="sr-only" wire:model.live="photos" accept="image/png,image/jpg,image/gif">
The component has a public property:
public $photos = [];
Upon clicking a submit
button, we apply validation to the field,
'photos' => 'nullable|array|max:3', // array 'photos.*' => [ 'required', 'image', 'max:10240', // 10MB new ExplicitImage(), ],
ExplicitImage
is a validation rule.
Temporary Files
We upload temporary files to a Minio bucket which resides on the server. The bucket is defined as an uploads
bucket for Laravel. This is done in config/filesystems.php
.
ApyHub Results for Image Content
We use curl
to call ApyHub. The response looks like this:
{ "data": { "apyhub": { "adult": { "adultScore": 0.0025164163671433926, "goreScore": 0.0014069777680560946, "isAdultContent": false, "isGoryContent": false, "isRacyContent": false, "racyScore": 0.0032450903672724962 }, "metadata": { "format": "Png", "height": 1024, "width": 1024 } } } }
The Validation Rule
<?php namespace App\Rules; use Closure; use Illuminate\Contracts\Validation\ValidationRule; use CURLFile; use CURLStringFile; use Illuminate\Support\Facades\Storage; class ExplicitImage implements ValidationRule { /** * Run the validation rule. * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ public function validate(string $attribute, mixed $value, Closure $fail): void { // path to temporary uploaded file in uploads disk $path = config('livewire')['temporary_file_upload']['directory'] . DIRECTORY_SEPARATOR . $value->getFilename(); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, "https://api.apyhub.com/ai/image/detect/explicit-content/file" ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'apy-token: ' . config('app.APYHUB_TOKEN'), 'content-type: multipart/form-data' ]); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'file' => new CURLStringFile(Storage::disk('uploads')->get($path), $value->getFilename(), $value->getMimeType()), 'requested_service' => 'apyhub', ]); $response = curl_exec($ch); curl_close($ch); if ($response === false) { $error = curl_error($ch); logger()->warning("ApyHub image moderation CURL Error: $error"); return; } else { // Process the successful CURL call here. $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($statusCode == 200) { // The request was successful $data = json_decode($response, true); // $response = $data['data']['apyhub']['adult']; if ($response['isAdultContent'] || $response['isGoryContent'] || $response['isRacyContent']) { $fail('validation.' . 'explicit_image')->translate(); } } else { // The server responded with an error logger()->warning("ApyHub image moderation HTTP Error: $statusCode"); return; } } } }
Top comments (0)