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)