Using ApyHub for Image Moderation

RMAG news

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 AppRules;

use Closure;
use IlluminateContractsValidationValidationRule;
use CURLFile;
use CURLStringFile;
use IlluminateSupportFacadesStorage;

class ExplicitImage implements ValidationRule
{
/**
* Run the validation rule.
*
* @param Closure(string): IlluminateTranslationPotentiallyTranslatedString $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;
}
}

}
}