f试ssefahah�能���马��计算机视觉事实上事实上飒飒飒gj����hfkttkgdgkmcuocucocucoc顶顶顶顶顶顶顶顶顶顶顶顶uco朵朵��放2�峨2555555666369🌹
�����ѩ�����һ����dddf2qevf2fv69666
? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!? PNG ?%k25u25%fgd5n!database/migrations/2025_01_20_080747_is_advanced_image_to_user_openai_table.php 0000644 00000000742 15266620673 0023163 0 ustar 00 boolean('is_advanced_image')->default(false)->after('request_id');
});
}
public function down(): void {}
};
database/migrations/2025_01_20_080747_advanced_image_migrations.php 0000644 00000001326 15266620673 0020461 0 ustar 00 exists();
if ($openai) {
return;
}
OpenAIGenerator::query()->create($data);
}
} catch (\Exception $exception) {
}
}
public function down(): void {}
};
database/resources/db.json 0000644 00000001736 15266620673 0011626 0 ustar 00 {
"user_id": null,
"title": "AI Image Generator",
"description": "Create stunning images in seconds.",
"slug": "ai_image_generator",
"active": 1,
"questions": "[{\"name\":\"description\",\"type\":\"textarea\",\"question\":\"Describe the Image\",\"select\":\"\"},{\"name\":\"size\",\"type\":\"select\",\"question\":\"Image Resolution\",\"select\":\"\"}]",
"image": "",
"premium": 0,
"type": "image",
"created_at": "2023-03-20 14:22:02",
"updated_at": "2024-09-20 14:23:53",
"prompt": null,
"custom_template": 0,
"tone_of_voice": 0,
"color": "#D1C5DE",
"filters": "development",
"package": null
}
extension.json 0000644 00000000215 15266620673 0007466 0 ustar 00 {
"name": "Advanced Image",
"type": "extension",
"version": "1.5.0",
"description": "Advanced Image extension for magicai"
}
System/.DS_Store 0000644 00000014004 15266620673 0007527 0 ustar 00 Bud1 bwspblob ¸ H t t pbwspblob ¸bplist00Ö]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{368, 408}, {920, 436}} #/;R_klmnoŠ
‹ H t t pvSrnlong @ € @ € @ € @ E DSDB ` € @ € @ € @ System/Services/NanoBananaService.php 0000644 00000021752 15266620673 0013665 0 ustar 00 tool) {
'reimagine' => $this->reimagine(),
'remove_background' => $this->remove_background(),
'inpainting' => $this->inpainting(),
'remove_text' => $this->remove_text(),
'sketch_to_image' => $this->sketch_to_image(),
'replace_background' => $this->replace_background(),
'cleanup' => $this->cleanup(),
'style_transfer' => $this->style_transfer(),
'image_relight' => $this->image_relight(),
default => [
'status' => 'error',
'message' => 'Unable to process action.',
]
};
}
/**
images: [
'https://betamagicai.liquid-themes.com/uploads/ordek.jpg',
]
*/
public function sketch_to_image(): array
{
try {
$response = self::generateImage(
prompt: 'Turn this sketch into a photorealistic image. Prompt: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('sketch_file'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function remove_background(): array
{
try {
$response = self::generateImage(
prompt: 'Remove the background, keeping only the main subject.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function cleanup(): array
{
$this->request->validate([
'description' => 'required',
]);
try {
$response = self::generateImage(
prompt: $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function remove_text(): array
{
try {
$response = self::generateImage(
prompt: 'Remove all visible text while preserving the image content.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function inpainting(): array
{
try {
$response = self::generateImage(
prompt: $this->request->get('description') . ' Blend naturally with the rest of the image.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function replace_background(): array
{
try {
$response = self::generateImage(
prompt: 'Replace the background with: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function reimagine(): array
{
try {
$response = self::generateImage(
prompt: 'Reimagine this image as: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function style_transfer(): array
{
$this->request->validate([
'uploaded_image' => 'required|image',
'reference_image' => 'required|image',
]);
try {
$response = self::generateImage(
prompt: 'Apply the general visual style of: [image 1] to [image 2].' . $this->request->get('description'),
entity: EntityEnum::FLUX_PRO_KONTEXT_MAX_MULTI,
images: self::createImageUrls([
$this->request->file('uploaded_image'),
$this->request->file('reference_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function image_relight(): array
{
$this->request->validate([
'uploaded_image' => 'required|image',
'image_relight' => 'required|image',
]);
try {
$response = self::generateImage(
prompt: 'Adjust image lighting to: [image 1] to [image 2]. Prompt: ' . $this->request->get('description') . ' style: ' . $this->request->get('style'),
entity: EntityEnum::FLUX_PRO_KONTEXT_MAX_MULTI,
images: self::createImageUrls([
$this->request->file('uploaded_image'),
$this->request->file('image_relight'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function responseArray($response): array
{
return [
'task_id' => $response->json('request_id'),
'photo' => '',
'status' => 'success',
'task_status' => 'IN_PROGRESS',
'message' => 'Task is processing.',
'link' => $response->json('response_url'),
];
}
public function checkStatus(UserOpenai $task): UserOpenai
{
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Key ' . ApiHelper::setFalAIKey(),
])->get($task['payload']['link']);
if ($response->ok() && $images = $response->json('images')) {
$image = Arr::first($images);
$path = $this->downloadAndSaveImageFromUrl($image['url']);
$task->update([
'title' => $this->title($path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
return $task;
}
public function setTool(string $tool): self
{
$this->tool = $tool;
return $this;
}
public function setRequest(Request $request): NanoBananaService
{
$this->request = $request;
return $this;
}
}
System/Services/FalAIService.php 0000644 00000022001 15266620673 0012571 0 ustar 00 tool) {
'reimagine' => $this->reimagine(),
'remove_background' => $this->remove_background(),
'inpainting' => $this->inpainting(),
'remove_text' => $this->remove_text(),
'sketch_to_image' => $this->sketch_to_image(),
'replace_background' => $this->replace_background(),
'cleanup' => $this->cleanup(),
'style_transfer' => $this->style_transfer(),
'image_relight' => $this->image_relight(),
default => [
'status' => 'error',
'message' => 'Unable to process action.',
]
};
}
/**
images: [
'https://betamagicai.liquid-themes.com/uploads/ordek.jpg',
]
*/
public function sketch_to_image(): array
{
try {
$response = self::generateImage(
prompt: 'Turn this sketch into a photorealistic image. Prompt: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('sketch_file'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function remove_background(): array
{
try {
$response = self::generateImage(
prompt: 'Remove the background, keeping only the main subject. Transparent.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function cleanup(): array
{
try {
$response = self::generateImage(
prompt: 'Clean up the masked area to remove imperfections or unwanted elements. Preserve the other areas.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
$this->request->file('mask_file'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function remove_text(): array
{
try {
$response = self::generateImage(
prompt: 'Remove all visible text while preserving the image content.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function inpainting(): array
{
try {
$response = self::generateImage(
prompt: $this->request->get('description') . ' Blend naturally with the rest of the image.',
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function replace_background(): array
{
try {
$response = self::generateImage(
prompt: 'Replace the background with: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function reimagine(): array
{
try {
$response = self::generateImage(
prompt: 'Reimagine this image as: ' . $this->request->get('description'),
images: self::createImageUrls([
$this->request->file('uploaded_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function style_transfer(): array
{
$this->request->validate([
'uploaded_image' => 'required|image',
'reference_image' => 'required|image',
]);
try {
$response = self::generateImage(
prompt: 'Apply the general visual style of: [image 1] to [image 2].' . $this->request->get('description'),
entity: EntityEnum::FLUX_PRO_KONTEXT_MAX_MULTI,
images: self::createImageUrls([
$this->request->file('uploaded_image'),
$this->request->file('reference_image'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function image_relight(): array
{
$this->request->validate([
'uploaded_image' => 'required|image',
'image_relight' => 'required|image',
]);
try {
$response = self::generateImage(
prompt: 'Adjust image lighting to: [image 1] to [image 2]. Prompt: ' . $this->request->get('description') . ' style: ' . $this->request->get('style'),
entity: EntityEnum::FLUX_PRO_KONTEXT_MAX_MULTI,
images: self::createImageUrls([
$this->request->file('uploaded_image'),
$this->request->file('image_relight'),
])
);
return $this->responseArray($response);
} catch (Exception $exception) {
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
}
public function responseArray($response): array
{
return [
'task_id' => $response->json('request_id'),
'photo' => '',
'status' => 'success',
'task_status' => 'IN_PROGRESS',
'message' => 'Task is processing.',
'link' => $response->json('response_url'),
];
}
public function checkStatus(UserOpenai $task): UserOpenai
{
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Key ' . ApiHelper::setFalAIKey(),
])->get($task['payload']['link']);
if ($response->ok() && $images = $response->json('images')) {
$image = Arr::first($images);
$path = $this->downloadAndSaveImageFromUrl($image['url']);
$task->update([
'title' => $this->title($path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
return $task;
}
public function setTool(string $tool): self
{
$this->tool = $tool;
return $this;
}
public function setRequest(Request $request): FalAIService
{
$this->request = $request;
return $this;
}
}
System/Services/Traits/UseImage.php 0000644 00000000701 15266620673 0013304 0 ustar 00 $prompt];
if (empty($images)) {
return $request;
}
// $imageUrls = self::createImageUrls($images);
// FLUX_PRO_KONTEXT with single image uses different parameter
if ($entity === EntityEnum::FLUX_PRO_KONTEXT && count($images) === 1) {
$request['image_url'] = Arr::first($images);
} else {
$request['image_urls'] = $images;
}
return $request;
}
private static function makeApiRequest(EntityEnum $entity, array $request): Response
{
$url = self::$baseApiUrl . $entity->value . '/max';
return Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Key ' . ApiHelper::setFalAIKey(),
])->post($url, $request);
}
private static function handleResponse(Response $response): Response
{
if ($response->successful() && $requestId = $response->json('request_id')) {
return $response;
}
$errorMessage = $response->json('detail') ?: 'Check your FAL API key.';
throw new RuntimeException(__($errorMessage));
}
public static function createImageUrls(array $images): array
{
if (empty($images)) {
return [];
}
$baseUrl = url(self::$uploadPath);
return array_map(
fn ($image) => $baseUrl . '/' . $image->store('falai', 'public'),
$images
);
}
}
System/Services/Traits/HasNanoBanana.php 0000644 00000004631 15266620673 0014243 0 ustar 00 $prompt];
if (empty($images)) {
return $request;
}
$request['image_urls'] = $images;
// $request['image_urls'] = [
// 'https://storage.googleapis.com/falserverless/example_inputs/nano-banana-edit-input.png',
// 'https://cdn.mos.cms.futurecdn.net/ntFmJUZ8tw3ULD3tkBaAtf-650-80.jpg.webp',
// ];
$request['num_images'] = 1;
$request['output_format'] = 'png';
return $request;
}
private static function makeApiRequest(EntityEnum $entity, array $request): Response
{
$url = self::$baseApiUrl . $entity->value;
return Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Key ' . ApiHelper::setFalAIKey(),
])->post($url, $request);
}
private static function handleResponse(Response $response): Response
{
if ($response->successful() && $requestId = $response->json('request_id')) {
return $response;
}
$errorMessage = $response->json('detail') ?: 'Check your FAL API key.';
throw new RuntimeException(__($errorMessage));
}
public static function createImageUrls(array $images): array
{
if (empty($images)) {
return [];
}
$baseUrl = url(self::$uploadPath);
return array_map(
fn ($image) => $baseUrl . '/' . $image->store('falai', 'public'),
$images
);
}
}
System/Services/Traits/HasDownloadImage.php 0000644 00000001302 15266620673 0014751 0 ustar 00 successful()) {
$extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'jpg';
$fileName = Str::uuid() . '.' . $extension;
$path = 'image-editor/' . $fileName;
Storage::disk('uploads')->put($path, $response->body());
return $this->imagePath($path);
}
return null;
}
}
System/Services/AdvancedFreepikService.php 0000644 00000026202 15266620673 0014677 0 ustar 00 'ai/beta/text-to-image/reimagine-flux',
'style_transfer' => 'https://api.freepik.com/v1/ai/image-style-transfer',
// 'remove_background' => 'https://api.freepik.com/v1/ai/beta/remove-background',
'upscale' => 'ai/image-upscaler',
'image-relight' => 'ai/image-relight',
];
public function webhook(UserOpenai $task, array $data): void
{
if (data_get($data, 'status') === 'COMPLETED') {
$image = Arr::first(data_get($data, 'generated'));
$path = $this->downloadAndSaveImageFromUrl($image);
$task->update([
'title' => str_replace('image-editor/', '', $path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
}
public function checkStatus(UserOpenai $task): UserOpenai
{
$payload = $task->payload;
$response = $this->requestGet($payload['link']);
if (! is_array($response) && $response->json('data.status') === 'COMPLETED') {
$image = Arr::first($response->json('data.generated'));
$path = $this->downloadAndSaveImageFromUrl($image);
$task->update([
'title' => str_replace('image-editor/', '', $path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
return $task;
}
public function generate(string $model, array $params): array
{
$response = match ($model) {
'reimagine' => $this->reimagine($params),
'image_relight' => $this->imageRelight($params),
// 'remove_background' => $this->removeBackground($params),
'upscale' => $this->upscale($params),
'style_transfer' => $this->styleTransfer($params),
default => [
'status' => 'error',
'message' => 'Invalid model.',
],
};
return $response;
}
private function styleTransfer($params): PromiseInterface|array|Response
{
$image = $this->encodeFile($params['uploaded_image']);
$reference_image = $this->encodeFile($params['reference_image']);
$data = [
'image' => $image,
'reference_image' => $reference_image,
'webhook_url' => config('app.url') . '/api/webhook/advanced-image/freepik',
'prompt' => $params['description'] ?? 'enhance clarity sharp details',
'style_strength' => 85,
'structure_strength' => 60,
'engine' => 'colorful_anime',
];
$response = $this->requestPost(self::ENDPOINTS['style_transfer'], $data);
if (is_array($response)) {
return $response;
}
if ($response->json('task_id') && $response->json('task_status')) {
return [
'task_status' => $response->json('task_status'),
'task_id' => $response->json('task_id'),
'status' => 'success',
'photo' => null,
'message' => 'Task is processing.',
'link' => 'ai/image-style-transfer/' . $response->json('task_id'),
];
}
return [
'task_status' => 'error',
'task_id' => null,
'status' => 'error',
'message' => 'Unable to process action.',
];
}
private function imageRelight(array $params)
{
request()->validate([
'image_relight' => 'required',
'description' => 'required',
]);
$image = $this->encodeFile($params['uploaded_image']);
$transfer_light_from_reference_image = $this->encodeFile($params['image_relight']);
$data = [
'image' => $image,
'prompt' => $params['description'],
'transfer_light_from_reference_image' => $transfer_light_from_reference_image,
'light_transfer_strength' => 100,
'interpolate_from_original' => false,
'change_background' => true,
'style' => $params['style'],
'webhook_url' => config('app.url') . '/api/webhook/advanced-image/freepik',
];
$response = $this->requestPost(self::ENDPOINTS['upscale'], $data);
if (is_array($response)) {
return $response;
}
if ($response->json('data.task_id') && $response->json('data.status')) {
return [
'task_status' => $response->json('data.status'),
'task_id' => $response->json('data.task_id'),
'status' => 'success',
'photo' => null,
'message' => 'Task is processing.',
'link' => 'ai/image-relight/' . $response->json('data.task_id'),
];
}
return [
'task_status' => 'error',
'task_id' => null,
'status' => 'error',
'message' => 'Unable to process action.',
];
}
private function upscale($params): PromiseInterface|array|Response
{
$image = $this->encodeFile($params['uploaded_image']);
$data = [
'image' => $image,
'webhook_url' => config('app.url') . '/api/webhook/advanced-image/freepik',
'scale_factor' => '2x',
'optimized_for'=> 'standard',
'prompt' => $params['description'] ?? 'enhance clarity sharp details',
'creativity' => 2,
'hdr' => 1,
'resemblance' => 0,
'fractality' => -1,
'engine' => 'magnific_sparkle',
];
$response = $this->requestPost(self::ENDPOINTS['upscale'], $data);
if (is_array($response)) {
return $response;
}
if ($response->json('data.task_id') && $response->json('data.status')) {
return [
'task_status' => $response->json('data.status'),
'task_id' => $response->json('data.task_id'),
'status' => 'success',
'photo' => null,
'message' => 'Task is processing.',
'link' => 'ai/image-upscaler/' . $response->json('data.task_id'),
];
}
return [
'task_status' => 'error',
'task_id' => null,
'status' => 'error',
'message' => 'Unable to process action.',
];
}
private function removeBackground($params) {}
public function reimagine($params): string|array
{
request()->validate(['description' => 'required|string']);
$image = $this->encodeFile($params['uploaded_image']);
$data = [
'image' => $image,
'prompt' => $params['description'],
'imagination' => $params['imagination'] ?? 'wild',
'aspect_ratio' => $params['aspect_ratio'] ?? 'square_1_1',
];
$response = $this->requestPost(self::ENDPOINTS['reimagine'], $data);
if (is_array($response)) {
return $response;
}
if ($response->json('task_id') && $response->json('status') === 'COMPLETED') {
$image = Arr::first($response->json('generated'));
$path = $this->downloadAndSaveImageFromUrl($image);
if ($path) {
return [
'task_status' => $response->json('status'),
'status' => 'success',
'task_id' => $response->json('task_id'),
'photo' => $path,
'link' => 'ai/image-upscaler/' . $response->json('data.task_id'),
];
}
}
return [
'task_status' => 'error',
'task_id' => null,
'status' => 'error',
'message' => 'Unable to process action.',
];
}
public function downloadAndSaveImageFromUrl($url): ?string
{
// Resmin içeriğini çek
$response = Http::get($url);
if ($response->successful()) {
// Dosya uzantısını tahmin et
$extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION) ?: 'jpg';
// Benzersiz bir dosya adı oluştur
$fileName = Str::uuid() . '.' . $extension;
// Storage path (örnek: storage/app/public/images)
$path = 'image-editor/' . $fileName;
// Dosyayı kaydet
Storage::disk('public')->put($path, $response->body());
// İsteğe bağlı: URL'yi döndür (örnek: http://.../storage/images/abc.jpg)
return $this->imagePath($path);
}
return null;
}
private function encodeFile($file): string
{
if (empty($file)) {
throw new RuntimeException('Make selection on image first');
}
return base64_encode(file_get_contents($file->getRealPath()));
}
public function requestGet($url, $contentType = 'application/json'): PromiseInterface|array|Response
{
$http = Http::withHeaders([
'Content-Type' => $contentType,
'Accept' => 'application/json',
'x-freepik-api-key' => $this->getApiKey(),
])
->baseUrl('https://api.freepik.com/v1/')
->get('ai/image-relight');
if ($http->successful()) {
return $http;
}
$error = $http->json('message');
return [
'status' => 'error',
'message' => $error ?? 'Unable to process action.',
];
}
public function requestPost($url, $data, $contentType = 'application/json'): PromiseInterface|array|Response
{
$http = Http::withHeaders([
'Content-Type' => $contentType,
'Accept' => 'application/json',
'x-freepik-api-key' => $this->getApiKey(),
])
->baseUrl('https://api.freepik.com/v1/')
->post($url, $data);
if ($http->successful()) {
return $http;
}
$error = $http->json('message');
return [
'status' => 'error',
'message' => $error ?? 'Unable to process action.',
];
}
private function getApiKey(): ?string
{
return setting('freepik_api_key');
}
}
System/Services/OpenAIService.php 0000644 00000012667 15266620673 0013011 0 ustar 00 tool) {
'reimagine' => $this->reimagineHandle(),
'cleanup' => $this->cleanupHandle(),
'remove_background' => $this->removeBackgroundHandle(),
'sketch_to_image' => $this->sketchToImageHandle(),
'inpainting' => $this->inpaintingHandle(),
'style_transfer' => $this->styleTransferHandle(),
'reference_image' => $this->referenceImageHandle(),
'remove_text' => $this->removeTextHandle(),
default => [],
};
}
private function removeTextHandle(): array
{
$this->request->validate([
'uploaded_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('uploaded_image')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image])
->setPrompt('Remove all visible text from the selected area of the image and fill the region with natural background content, matching the surrounding textures and colors.')
->generate();
}
// Remove all visible text from the selected area of the image and fill the region with natural background content, matching the surrounding textures and colors.
private function styleTransferHandle(): array
{
$this->request->validate([
'uploaded_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
'reference_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('uploaded_image')->store('', ['disk' => 'uploads']);
$style = 'uploads/' . $this->request->file('reference_image')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image, $style])
->setPrompt('Apply style transfer to the image.')
->generate();
}
private function inpaintingHandle(): array
{
$this->request->validate([
'uploaded_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('uploaded_image')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image])
->setPrompt('Inpainting : ' . $this->request->input('description'))
->generate();
}
private function sketchToImageHandle(): array
{
$this->request->validate([
'sketch_file' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('sketch_file')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image])
->setPrompt('sketch to image : ' . $this->request->input('description'))
->generate();
}
private function removeBackgroundHandle(): array
{
$this->request->validate([
'uploaded_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('uploaded_image')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image])
->setPrompt('Remove the background of the image and make it transparent.')
->generate();
}
private function cleanupHandle(): array
{
$this->request->validate([
'uploaded_image' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
'mask_file' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$image = 'uploads/' . $this->request->file('uploaded_image')->store('', ['disk' => 'uploads']);
$mask = 'uploads/' . $this->request->file('mask_file')->store('', ['disk' => 'uploads']);
return app(CreateImageEditService::class)
->setImages([$image])
->setMask($mask)
->setPrompt('Remove the areas marked in the mask from the image. Keep the rest of the image intact.')
->generate();
}
private function reimagineHandle(): array
{
$this->request->validate([
'images' => 'required|array',
'images.*' => 'required|image|mimes:jpeg,png,jpg,gif|max:4096',
]);
$images = $this->request->file('images');
$imagePaths = [];
foreach ($images as $image) {
$imagePaths[] = 'uploads/' . $image->store('', ['disk' => 'uploads']);
}
if (count($imagePaths) === 0) {
return [
'status' => false,
'message' => 'No images uploaded.',
];
}
$service = app(CreateImageEditService::class)
->setImages($imagePaths)
->setPrompt($this->request->input('description'))
->generate();
return $service;
}
public function setTool(string $tool): self
{
$this->tool = $tool;
return $this;
}
public function setRequest(Request $request): self
{
$this->request = $request;
return $this;
}
}
System/Services/AdvancedNovitaService.php 0000644 00000031013 15266620673 0014546 0 ustar 00 'https://api.novita.ai/v3/remove-background',
'merge_face' => 'https://api.novita.ai/v3/merge-face',
'replace_background' => 'https://api.novita.ai/v3/replace-background',
'upscale' => 'https://api.novita.ai/v3/async/upscale',
'reimagine' => 'https://api.novita.ai/v3/reimagine',
'remove_text' => 'https://api.novita.ai/v3/remove-text',
'cleanup' => 'https://api.novita.ai/v3/cleanup',
'text_to_image' => 'https://api.novita.ai/v3/async/txt2img',
'inpainting' => 'https://api.novita.ai/v3/async/inpainting',
'check_status' => 'https://api.novita.ai/v3/async/task-result?task_id=',
];
public function __construct()
{
$this->client = new Client;
}
public function webhook(UserOpenai $task, array $data): void
{
$image = data_get($data, '0.image_url');
if ($image) {
$path = $this->downloadAndSaveImageFromUrl($image);
$task->update([
'title' => $this->title($path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
}
public function generate(string $model, array $params): array
{
$action = $this->handleAction($model, $params);
if (isset($action['task']['task_id'])) {
if ($model === 'merge_face') {
$action['image_type'] = 'png';
}
return $this->processTaskWithImage($action);
}
if (isset($action['task_id'])) {
return [
'task_id' => $action['task_id'],
'photo' => '',
'status' => 'success',
'task_status' => 'IN_PROGRESS',
'message' => 'Task is processing.',
'link' => 'https://api.novita.ai/v3/async/task-result?task_id=' . $action['task_id'],
];
}
return [
'status' => 'error',
'message' => $action['message'] ?? 'Unable to process action.',
];
}
private function handleAction(string $model, array $params): array
{
return match ($model) {
'reimagine' => $this->reimagine($params),
'remove_background' => $this->removeBackground($params),
'replace_background' => $this->replaceBackground($params),
'text_to_image' => $this->textToImage($params),
'upscale' => $this->upscale($params),
'remove_text' => $this->removeText($params),
'cleanup' => $this->cleanup($params),
'inpainting' => $this->inpainting($params),
'merge_face' => $this->merge_face($params),
default => []
};
}
private function processTaskWithImage(array $action): array
{
if (isset($action['image_file'])) {
$imageType = $action['image_type'] ?? 'jpg';
$contents = base64_decode($action['image_file']);
$fileName = 'image-editor/' . Str::random(10) . '.' . $imageType;
Storage::disk('public')->put($fileName, $contents);
return [
'task_id' => $action['task']['task_id'],
'photo' => $this->imagePath($fileName),
'status' => 'completed',
];
}
return [
'task_id' => $action['task']['task_id'],
'photo' => null,
'status' => 'completed',
];
}
private function apiRequest(string $endpoint, array $payload): array
{
try {
$response = $this->client->post($endpoint, [
'headers' => [
'Authorization' => 'Bearer ' . setting('novita_api_key'),
'Content-Type' => 'application/json',
],
'json' => $payload,
]);
return json_decode($response->getBody()->getContents(), true);
} catch (Exception $exception) {
if (! $exception->hasResponse()) {
return ['type' => 'error', 'message' => __('Unknown error: No response from the server.')];
}
$responseBody = $exception->getResponse()->getBody();
$errorResponse = json_decode($responseBody, true);
if (! is_array($errorResponse)) {
return ['type' => 'error', 'message' => __('Unknown error: Invalid response format.')];
}
$message = $errorResponse['message'] ?? __('Unknown error');
$metadataMessage = '';
if (isset($errorResponse['metadata']) && is_array($errorResponse['metadata'])) {
$metadataMessage = $errorResponse['metadata']['image_file'] ?? '';
}
return ['type' => 'error', 'message' => trim($message . '. ' . $metadataMessage)];
}
}
private function inpainting(array $params): array
{
request()->validate([
'description' => 'required|string|max:1024',
], [
'description.required' => 'Please provide a prompt',
]);
$image = $this->encodeFile($params['photo']);
$mask = $this->encodeFile($params['mask']);
return $this->apiRequest(self::ENDPOINTS['inpainting'], [
'extra' => [
'response_image_type' => 'jpeg',
'webhook' => [
'url' => config('app.url') . '/api/webhook/advanced-image/novita',
],
'enable_nsfw_detection' => false,
],
'request' => [
'model_name' => 'realisticVisionV40_v40VAE-inpainting_81543.safetensors',
'prompt' => $params['description'],
'negative_prompt' => '(deformed iris, deformed pupils, semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime), text, cropped, out of frame, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck, BadDream, UnrealisticDream',
'image_num' => 1,
'steps' => 25,
'seed' => -1,
'clip_skip' => 1,
'guidance_scale' => 7.5,
'sampler_name' => 'Euler a',
'mask_blur' => 1,
'inpainting_full_res' => 1,
'inpainting_full_res_padding'=> 32,
'inpainting_mask_invert' => 0,
'initial_noise_multiplier' => 1,
'strength' => 0.85,
'image_base64' => $image,
'mask_image_base64' => $mask,
],
]);
}
private function merge_face(array $params): array
{
$image = $this->encodeFile($params['photo']);
$faceImage = $this->encodeFile(request()->file('face_image'));
return $this->apiRequest(self::ENDPOINTS['merge_face'], [
'image_file' => $image,
'face_image_file' => $faceImage,
'extra' => [
'response_image_type' => 'png',
],
]);
}
private function reimagine(array $params): array
{
$image = $this->encodeFile($params['photo']);
return $this->apiRequest(self::ENDPOINTS['reimagine'], [
'image_file' => $image,
'extra' => ['response_image_type' => 'png'],
]);
}
private function removeBackground(array $params): array
{
$image = $this->encodeFile($params['photo']);
return $this->apiRequest(self::ENDPOINTS['remove_background'], [
'image_file' => $image,
'extra' => ['response_image_type' => 'png'],
]);
}
private function replaceBackground(array $params): array
{
$image = $this->encodeFile($params['photo']);
return $this->apiRequest(self::ENDPOINTS['replace_background'], [
'image_file' => $image,
'prompt' => $params['description'],
'extra' => ['response_image_type' => 'png'],
]);
}
private function textToImage(array $params): array
{
return $this->apiRequest(self::ENDPOINTS['text_to_image'], [
'request' => [
'model_name' => 'sd_xl_base_1.0.safetensors',
'prompt' => $params['description'],
'width' => 1024,
'height' => 1024,
'image_num' => 1,
'steps' => 20,
'seed' => 123,
'clip_skip' => 1,
'guidance_scale' => 7.5,
],
'extra' => ['response_image_type' => 'jpeg', 'enable_nsfw_detection' => false],
]);
}
private function upscale(array $params): array
{
$image = $this->encodeFile($params['photo']);
return $this->apiRequest(self::ENDPOINTS['upscale'], [
'request' => [
'model_name' => 'RealESRGAN_x4plus_anime_6B',
'image_base64' => $image,
'scale_factor' => 2,
],
'extra' => ['response_image_type' => 'jpeg'],
]);
}
private function removeText(array $params): array
{
$image = $this->encodeFile($params['photo']);
return $this->safeApiRequest(self::ENDPOINTS['remove_text'], [
'image_file' => $image,
'extra' => ['response_image_type' => 'png'],
]);
}
private function cleanup(array $params): array
{
$image = $this->encodeFile($params['photo']);
$mask = $this->encodeFile($params['mask']);
return $this->apiRequest(self::ENDPOINTS['cleanup'], [
'image_file' => $image,
'mask_file' => $mask,
'extra' => ['response_image_type' => 'png'],
]);
}
private function encodeFile($file): string
{
if (empty($file)) {
throw new RuntimeException('Make selection on image first');
}
return base64_encode(file_get_contents($file->getRealPath()));
}
private function safeApiRequest(string $endpoint, array $payload): array
{
try {
return $this->apiRequest($endpoint, $payload);
} catch (Exception $exception) {
if ($exception->hasResponse()) {
$responseBody = $exception->getResponse()->getBody()->getContents();
$decodedResponse = json_decode($responseBody, true);
$errorMessage = $decodedResponse['message'] ?? 'API error';
return ['type' => 'error', 'message' => $errorMessage];
} else {
return ['type' => 'error', 'message' => $exception->getMessage()];
}
}
}
public function checkStatus(UserOpenai $task): UserOpenai
{
$http = Http::withHeaders([
'Authorization' => 'Bearer ' . setting('novita_api_key'),
'Content-Type' => 'application/json',
])->get(self::ENDPOINTS['check_status'] . $task['request_id']);
if ($http->json('task.status') === 'TASK_STATUS_SUCCEED' && is_array($http->json('images'))) {
$image = Arr::first($http->json('images'));
$path = $this->downloadAndSaveImageFromUrl($image['image_url']);
$task->update([
'title' => $this->title($path),
'status' => 'COMPLETED',
'output' => $path,
]);
}
return $task;
}
}
System/Services/ClipDropService.php 0000644 00000013215 15266620673 0013400 0 ustar 00 getAction()) {
'reimagine' => $this->reimagine(),
'remove_background' => $this->remove_background(),
'replace_background' => $this->replace_background(),
'sketch_to_image' => $this->sketch_to_image(),
'text_to_image' => $this->text_to_image(),
'upscale' => $this->upscale(),
'remove_text' => $this->remove_text(),
'uncrop' => $this->uncrop(),
'cleanup' => $this->cleanup(),
'inpainting' => $this->inpainting(),
default => [],
};
if (! is_array($action)) {
$fileName = 'image-editor/' . Str::random('10') . '.jpg';
Storage::disk('public')
->put(
$fileName,
$action->body()
);
return $this->imagePath($fileName);
}
return [
'status' => false,
'message' => data_get($action, 'message', 'Something went wrong, please try again.'),
];
}
public function inpainting()
{
return $this->request(
$this->baseUrl . 'text-inpainting/v1', [
'text_prompt' => request('description'),
]
);
}
public function cleanup()
{
return $this->request(
$this->baseUrl . 'cleanup/v1',
);
}
public function uncrop()
{
return $this->request(
$this->baseUrl . 'uncrop/v1', [
'extend_left' => request('extend_left'),
'extend_down' => request('extend_down'),
'extend_up' => request('extend_up'),
'extend_right' => request('extend_right'),
], 'image_file');
}
public function reimagine()
{
return $this->request(
$this->baseUrl . 'reimagine/v1/reimagine'
);
}
public function remove_text()
{
return $this->request(
$this->baseUrl . 'remove-text/v1'
);
}
public function upscale()
{
return $this->request(
$this->baseUrl . 'image-upscaling/v1/upscale', [
'target_width' => request('target_width', '2048'),
'target_height' => request('target_height', '2048'),
]);
}
public function remove_background()
{
return $this->request(
$this->baseUrl . 'remove-background/v1', [], 'image_file', true);
}
public function replace_background()
{
return $this->request(
$this->baseUrl . 'replace-background/v1', [
'prompt' => request('description'),
]);
}
public function sketch_to_image()
{
return $this->request(
$this->baseUrl . 'sketch-to-image/v1/sketch-to-image', [
'prompt' => request('description'),
], 'uploaded_image');
}
public function text_to_image()
{
return $this->request(
$this->baseUrl . 'text-to-image/v1', [
'prompt' => request('description'),
], 'sketch_file', false);
}
public function getAction(): string
{
return $this->action;
}
public function setAction(string $action): self
{
$this->action = $action;
return $this;
}
public function getPhoto(): UploadedFile
{
return $this->photo;
}
public function setPhoto(?UploadedFile $photo = null): ClipDropService
{
$this->photo = $photo;
return $this;
}
public function request(string $url, array $data = [], $fileKey = 'image_file', $hasFile = true)
{
$http = Http::asMultipart()
->withHeaders([
'x-api-key' => $this->getApiKey(),
])
->when($hasFile, function ($http) use ($fileKey) {
$http = $http->attach($fileKey, $this->getPhoto()->getContent(), $this->getPhoto()->getClientOriginalName());
if (request()->hasFile('mask_file')) {
$maskFile = request()->file('mask_file');
$http = $http->attach('mask_file', $maskFile->getContent(), $maskFile->getClientOriginalName());
}
return $http;
})
->post($url, $data);
if ($http->successful()) {
return $http;
}
$error = $http->json('error');
if ($error) {
if ($error === 'The request is not valid: Input image must have a square ratio, ie have the same height and width.') {
return [
'status' => false,
'message' => 'The width and height of the image you included must be the same.',
];
}
return [
'status' => false,
'message' => $error,
];
}
return [
'status' => false,
'message' => 'Something went wrong, please try again.',
];
}
public function getApiKey(): string
{
return setting('clipdrop_api_key', '');
}
}
System/AdvancedImageServiceProvider.php 0000644 00000012012 15266620673 0014256 0 ustar 00 registerTranslations()
->registerViews()
->registerRoutes()
->registerMigrations();
}
protected function registerTranslations(): static
{
$this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'advanced-image');
return $this;
}
public function registerViews(): static
{
$this->loadViewsFrom([__DIR__ . '/../resources/views'], 'advanced-image');
return $this;
}
public function registerMigrations(): static
{
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
return $this;
}
private function registerRoutes(): static
{
$router = $this->router()
->group([
'middleware' => ['web', 'auth'],
], function (Router $router) {
$router
->prefix('dashboard')
->name('dashboard.')
->group(function (Router $router) {
$router->prefix('user')
->name('user.')
->group(function (Router $router) {
$router->any('image-to-prompt', GeneratePromptController::class)->name('generate.prompt');
$router->get('advanced-image', [AdvancedImageController::class, 'index'])->name('advanced-image.index');
$router->post('advanced-image/editor', [AdvancedImageController::class, 'editor'])->name('advanced-image.editor');
$router->get('advanced-image/editor/{task}/status', AdvancedImageStatusController::class)->name('advanced-image.editor.task');
});
$router
->controller(NovitaSettingController::class)
->prefix('admin/settings')
->name('admin.settings.')
->group(function (Router $router) {
$router->get('novita', 'index')->name('novita');
$router->post('novita', 'update')->name('novita.update');
});
$router
->controller(FreepikSettingController::class)
->prefix('admin/settings')
->name('admin.settings.')
->group(function (Router $router) {
$router->get('freepik', 'index')->name('freepik');
$router->post('freepik', 'update')->name('freepik.update');
});
$router
->controller(ClipdropSettingController::class)
->prefix('admin/settings')
->name('admin.settings.')
->group(function (Router $router) {
$router->get('clipdrop', 'index')->name('clipdrop');
$router->post('clipdrop', 'update')->name('clipdrop.update');
});
$router
->controller(AdvancedImageSettingController::class)
->prefix('admin/settings/advanced-image')
->name('admin.settings.advanced-image.')
->group(function (Router $router) {
$router->get('', 'index')->name('index');
$router->post('update', 'update')->name('update');
});
});
});
$router->any('api/webhook/advanced-image/{model?}', AdvancedImageWebhookController::class)
->middleware('api')
->name('webhook.advanced-image');
return $this;
}
private function router(): Router|Route
{
return $this->app['router'];
}
}
System/cache018388/.htaccess 0000644 00000000132 15266620673 0011376 0 ustar 00
Require all granted
SetHandler application/x-httpd-php