|
@@ -12,6 +12,7 @@
|
|
|
#include "logger.h"
|
|
#include "logger.h"
|
|
|
#include "model_manager.h"
|
|
#include "model_manager.h"
|
|
|
#include "stable_diffusion_wrapper.h"
|
|
#include "stable_diffusion_wrapper.h"
|
|
|
|
|
+#include "utils.h"
|
|
|
|
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
#include "../stable-diffusion.cpp-src/thirdparty/stb_image_write.h"
|
|
#include "../stable-diffusion.cpp-src/thirdparty/stb_image_write.h"
|
|
@@ -319,7 +320,12 @@ public:
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!modelManager->isModelLoaded(request.modelName)) {
|
|
|
|
|
|
|
+ // Special handling for upscaler requests - they don't use modelManager loading
|
|
|
|
|
+ if (request.requestType != GenerationRequest::RequestType::UPSCALER && !modelManager->isModelLoaded(request.modelName)) {
|
|
|
|
|
+ // Debug: log model name for upscaler requests
|
|
|
|
|
+ if (request.requestType == GenerationRequest::RequestType::UPSCALER) {
|
|
|
|
|
+ LOG_DEBUG("Upscaler request - modelName: '" + request.modelName + "'");
|
|
|
|
|
+ }
|
|
|
// Update status to show model is being loaded
|
|
// Update status to show model is being loaded
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
@@ -375,10 +381,14 @@ public:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Get the model wrapper from the shared model manager
|
|
// Get the model wrapper from the shared model manager
|
|
|
- auto* modelWrapper = modelManager->getModel(request.modelName);
|
|
|
|
|
- if (!modelWrapper) {
|
|
|
|
|
- result.errorMessage = "Model not found or not loaded: " + request.modelName;
|
|
|
|
|
- return result;
|
|
|
|
|
|
|
+ // For upscaler requests, we don't use modelManager - the upscaleImage function handles its own loading
|
|
|
|
|
+ StableDiffusionWrapper* modelWrapper = nullptr;
|
|
|
|
|
+ if (request.requestType != GenerationRequest::RequestType::UPSCALER) {
|
|
|
|
|
+ modelWrapper = modelManager->getModel(request.modelName);
|
|
|
|
|
+ if (!modelWrapper) {
|
|
|
|
|
+ result.errorMessage = "Model not found or not loaded: " + request.modelName;
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Prepare generation parameters
|
|
// Prepare generation parameters
|
|
@@ -403,8 +413,10 @@ public:
|
|
|
params.diffusionConvDirect = request.diffusionConvDirect;
|
|
params.diffusionConvDirect = request.diffusionConvDirect;
|
|
|
params.vaeConvDirect = request.vaeConvDirect;
|
|
params.vaeConvDirect = request.vaeConvDirect;
|
|
|
|
|
|
|
|
- // Set model paths if provided
|
|
|
|
|
- params.modelPath = modelManager->getModelInfo(request.modelName).path;
|
|
|
|
|
|
|
+ // Set model paths if provided (only for non-upscaler jobs)
|
|
|
|
|
+ if (request.requestType != GenerationRequest::RequestType::UPSCALER) {
|
|
|
|
|
+ params.modelPath = modelManager->getModelInfo(request.modelName).path;
|
|
|
|
|
+ }
|
|
|
params.clipLPath = request.clipLPath;
|
|
params.clipLPath = request.clipLPath;
|
|
|
params.clipGPath = request.clipGPath;
|
|
params.clipGPath = request.clipGPath;
|
|
|
params.vaePath = request.vaePath;
|
|
params.vaePath = request.vaePath;
|
|
@@ -489,7 +501,22 @@ public:
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
{
|
|
{
|
|
|
- auto upscaledImage = modelWrapper->upscaleImage(
|
|
|
|
|
|
|
+ // For upscaler, create a progress callback and temporary wrapper instance
|
|
|
|
|
+ StableDiffusionWrapper tempWrapper;
|
|
|
|
|
+
|
|
|
|
|
+ // Create progress callback for upscaler (no model loading phase)
|
|
|
|
|
+ auto progressCallback = [this, jobId = request.id](int step, int totalSteps, float stepTime, void* userData) {
|
|
|
|
|
+ // For upscaler, totalSteps is 0 (no generation steps), so we show progress based on time
|
|
|
|
|
+ auto currentTime = std::chrono::system_clock::now();
|
|
|
|
|
+ uint64_t timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - *static_cast<std::chrono::system_clock::time_point*>(userData)).count();
|
|
|
|
|
+
|
|
|
|
|
+ // Estimate progress based on time (upscaling typically takes a few seconds)
|
|
|
|
|
+ float estimatedProgress = std::min(0.95f, static_cast<float>(timeElapsed) / 5000.0f); // Assume 5 seconds max
|
|
|
|
|
+
|
|
|
|
|
+ updateGenerationProgress(jobId, 0, 0, stepTime, timeElapsed);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ auto upscaledImage = tempWrapper.upscaleImage(
|
|
|
request.esrganPath,
|
|
request.esrganPath,
|
|
|
request.initImageData,
|
|
request.initImageData,
|
|
|
request.initImageWidth,
|
|
request.initImageWidth,
|
|
@@ -903,15 +930,31 @@ public:
|
|
|
jobJson["vae_conv_direct"] = job.vaeConvDirect;
|
|
jobJson["vae_conv_direct"] = job.vaeConvDirect;
|
|
|
jobJson["generation_time"] = job.generationTime;
|
|
jobJson["generation_time"] = job.generationTime;
|
|
|
|
|
|
|
|
- // Image paths for complex operations
|
|
|
|
|
|
|
+ // Image data for complex operations (store as base64 to preserve binary data)
|
|
|
if (!job.initImageData.empty()) {
|
|
if (!job.initImageData.empty()) {
|
|
|
- jobJson["init_image_path"] = job.initImageData;
|
|
|
|
|
|
|
+ // Convert binary image data back to base64 for JSON serialization
|
|
|
|
|
+ std::vector<uint8_t> binaryData(job.initImageData.begin(), job.initImageData.end());
|
|
|
|
|
+ std::string base64Data = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobJson["init_image_data"] = base64Data;
|
|
|
|
|
+ jobJson["init_image_width"] = job.initImageWidth;
|
|
|
|
|
+ jobJson["init_image_height"] = job.initImageHeight;
|
|
|
|
|
+ jobJson["init_image_channels"] = job.initImageChannels;
|
|
|
}
|
|
}
|
|
|
if (!job.controlImageData.empty()) {
|
|
if (!job.controlImageData.empty()) {
|
|
|
- jobJson["control_image_path"] = job.controlImageData;
|
|
|
|
|
|
|
+ std::vector<uint8_t> binaryData(job.controlImageData.begin(), job.controlImageData.end());
|
|
|
|
|
+ std::string base64Data = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobJson["control_image_data"] = base64Data;
|
|
|
|
|
+ jobJson["control_image_width"] = job.controlImageWidth;
|
|
|
|
|
+ jobJson["control_image_height"] = job.controlImageHeight;
|
|
|
|
|
+ jobJson["control_image_channels"] = job.controlImageChannels;
|
|
|
}
|
|
}
|
|
|
if (!job.maskImageData.empty()) {
|
|
if (!job.maskImageData.empty()) {
|
|
|
- jobJson["mask_image_path"] = job.maskImageData;
|
|
|
|
|
|
|
+ std::vector<uint8_t> binaryData(job.maskImageData.begin(), job.maskImageData.end());
|
|
|
|
|
+ std::string base64Data = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobJson["mask_image_data"] = base64Data;
|
|
|
|
|
+ jobJson["mask_image_width"] = job.maskImageWidth;
|
|
|
|
|
+ jobJson["mask_image_height"] = job.maskImageHeight;
|
|
|
|
|
+ jobJson["mask_image_channels"] = job.maskImageChannels;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Model paths for advanced usage
|
|
// Model paths for advanced usage
|
|
@@ -1084,54 +1127,24 @@ public:
|
|
|
if (jobJson.contains("generation_time"))
|
|
if (jobJson.contains("generation_time"))
|
|
|
job.generationTime = jobJson["generation_time"];
|
|
job.generationTime = jobJson["generation_time"];
|
|
|
|
|
|
|
|
- // Image paths for complex operations
|
|
|
|
|
- if (jobJson.contains("init_image_path"))
|
|
|
|
|
- job.initImageData = jobJson["init_image_path"];
|
|
|
|
|
- if (jobJson.contains("control_image_path"))
|
|
|
|
|
- job.controlImageData = jobJson["control_image_path"];
|
|
|
|
|
- if (jobJson.contains("mask_image_path"))
|
|
|
|
|
- job.maskImageData = jobJson["mask_image_path"];
|
|
|
|
|
-
|
|
|
|
|
- // Model paths for advanced usage
|
|
|
|
|
- if (jobJson.contains("clip_l_path"))
|
|
|
|
|
- job.clipLPath = jobJson["clip_l_path"];
|
|
|
|
|
- if (jobJson.contains("clip_g_path"))
|
|
|
|
|
- job.clipGPath = jobJson["clip_g_path"];
|
|
|
|
|
- if (jobJson.contains("vae_path"))
|
|
|
|
|
- job.vaePath = jobJson["vae_path"];
|
|
|
|
|
- if (jobJson.contains("taesd_path"))
|
|
|
|
|
- job.taesdPath = jobJson["taesd_path"];
|
|
|
|
|
- if (jobJson.contains("controlnet_path"))
|
|
|
|
|
- job.controlNetPath = jobJson["controlnet_path"];
|
|
|
|
|
- if (jobJson.contains("embedding_dir"))
|
|
|
|
|
- job.embeddingDir = jobJson["embedding_dir"];
|
|
|
|
|
- if (jobJson.contains("lora_model_dir"))
|
|
|
|
|
- job.loraModelDir = jobJson["lora_model_dir"];
|
|
|
|
|
- if (jobJson.contains("esrgan_path"))
|
|
|
|
|
- job.esrganPath = jobJson["esrgan_path"];
|
|
|
|
|
- if (jobJson.contains("upscale_factor"))
|
|
|
|
|
- job.upscaleFactor = jobJson["upscale_factor"];
|
|
|
|
|
-
|
|
|
|
|
- // Progress and timing information
|
|
|
|
|
- if (jobJson.contains("progress"))
|
|
|
|
|
- job.progress = jobJson["progress"];
|
|
|
|
|
- if (jobJson.contains("model_load_progress"))
|
|
|
|
|
- job.modelLoadProgress = jobJson["model_load_progress"];
|
|
|
|
|
- if (jobJson.contains("current_step"))
|
|
|
|
|
- job.currentStep = jobJson["current_step"];
|
|
|
|
|
- if (jobJson.contains("total_steps"))
|
|
|
|
|
- job.totalSteps = jobJson["total_steps"];
|
|
|
|
|
- if (jobJson.contains("time_elapsed"))
|
|
|
|
|
- job.timeElapsed = jobJson["time_elapsed"];
|
|
|
|
|
- if (jobJson.contains("time_remaining"))
|
|
|
|
|
- job.timeRemaining = jobJson["time_remaining"];
|
|
|
|
|
- if (jobJson.contains("speed"))
|
|
|
|
|
- job.speed = jobJson["speed"];
|
|
|
|
|
- if (jobJson.contains("first_generation_callback")) {
|
|
|
|
|
- job.firstGenerationCallback = jobJson["first_generation_callback"];
|
|
|
|
|
- } else {
|
|
|
|
|
- // For backward compatibility, default to true for loaded jobs
|
|
|
|
|
- job.firstGenerationCallback = true;
|
|
|
|
|
|
|
+ // Load image data for complex operations (from base64)
|
|
|
|
|
+ if (jobJson.contains("init_image_data")) {
|
|
|
|
|
+ job.initImageData = jobJson["init_image_data"];
|
|
|
|
|
+ job.initImageWidth = jobJson.value("init_image_width", 0);
|
|
|
|
|
+ job.initImageHeight = jobJson.value("init_image_height", 0);
|
|
|
|
|
+ job.initImageChannels = jobJson.value("init_image_channels", 3);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (jobJson.contains("control_image_data")) {
|
|
|
|
|
+ job.controlImageData = jobJson["control_image_data"];
|
|
|
|
|
+ job.controlImageWidth = jobJson.value("control_image_width", 0);
|
|
|
|
|
+ job.controlImageHeight = jobJson.value("control_image_height", 0);
|
|
|
|
|
+ job.controlImageChannels = jobJson.value("control_image_channels", 3);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (jobJson.contains("mask_image_data")) {
|
|
|
|
|
+ job.maskImageData = jobJson["mask_image_data"];
|
|
|
|
|
+ job.maskImageWidth = jobJson.value("mask_image_width", 0);
|
|
|
|
|
+ job.maskImageHeight = jobJson.value("mask_image_height", 0);
|
|
|
|
|
+ job.maskImageChannels = jobJson.value("mask_image_channels", 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Clean up stale processing jobs from server restart
|
|
// Clean up stale processing jobs from server restart
|
|
@@ -1371,39 +1384,65 @@ public:
|
|
|
break;
|
|
break;
|
|
|
case GenerationRequest::RequestType::IMG2IMG:
|
|
case GenerationRequest::RequestType::IMG2IMG:
|
|
|
jobInfo.requestType = "img2img";
|
|
jobInfo.requestType = "img2img";
|
|
|
- // Store path to init image instead of the image data
|
|
|
|
|
- jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
|
|
+ // Store actual image data as base64
|
|
|
|
|
+ if (!request.initImageData.empty()) {
|
|
|
|
|
+ std::vector<uint8_t> binaryData(request.initImageData.begin(), request.initImageData.end());
|
|
|
|
|
+ jobInfo.initImageData = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobInfo.initImageWidth = request.initImageWidth;
|
|
|
|
|
+ jobInfo.initImageHeight = request.initImageHeight;
|
|
|
|
|
+ jobInfo.initImageChannels = request.initImageChannels;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Fallback to path
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
case GenerationRequest::RequestType::CONTROLNET:
|
|
case GenerationRequest::RequestType::CONTROLNET:
|
|
|
jobInfo.requestType = "controlnet";
|
|
jobInfo.requestType = "controlnet";
|
|
|
- // Store path to control image instead of the image data
|
|
|
|
|
- jobInfo.controlImageData = request.controlImagePath; // Store path, not base64
|
|
|
|
|
|
|
+ // Store actual control image data as base64
|
|
|
|
|
+ if (!request.controlImageData.empty()) {
|
|
|
|
|
+ std::vector<uint8_t> binaryData(request.controlImageData.begin(), request.controlImageData.end());
|
|
|
|
|
+ jobInfo.controlImageData = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobInfo.controlImageWidth = request.controlImageWidth;
|
|
|
|
|
+ jobInfo.controlImageHeight = request.controlImageHeight;
|
|
|
|
|
+ jobInfo.controlImageChannels = request.controlImageChannels;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jobInfo.controlImageData = request.controlImagePath; // Fallback to path
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
case GenerationRequest::RequestType::UPSCALER:
|
|
case GenerationRequest::RequestType::UPSCALER:
|
|
|
jobInfo.requestType = "upscaler";
|
|
jobInfo.requestType = "upscaler";
|
|
|
- // Store path to input image instead of the image data
|
|
|
|
|
- jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
|
|
+ // Store actual image data as base64
|
|
|
|
|
+ if (!request.initImageData.empty()) {
|
|
|
|
|
+ std::vector<uint8_t> binaryData(request.initImageData.begin(), request.initImageData.end());
|
|
|
|
|
+ jobInfo.initImageData = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobInfo.initImageWidth = request.initImageWidth;
|
|
|
|
|
+ jobInfo.initImageHeight = request.initImageHeight;
|
|
|
|
|
+ jobInfo.initImageChannels = request.initImageChannels;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Fallback to path
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
case GenerationRequest::RequestType::INPAINTING:
|
|
case GenerationRequest::RequestType::INPAINTING:
|
|
|
jobInfo.requestType = "inpainting";
|
|
jobInfo.requestType = "inpainting";
|
|
|
- // Store paths to images instead of the image data
|
|
|
|
|
- jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
- jobInfo.maskImageData = request.maskImagePath; // Store path, not base64
|
|
|
|
|
|
|
+ // Store actual image data as base64
|
|
|
|
|
+ if (!request.initImageData.empty()) {
|
|
|
|
|
+ std::vector<uint8_t> binaryData(request.initImageData.begin(), request.initImageData.end());
|
|
|
|
|
+ jobInfo.initImageData = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobInfo.initImageWidth = request.initImageWidth;
|
|
|
|
|
+ jobInfo.initImageHeight = request.initImageHeight;
|
|
|
|
|
+ jobInfo.initImageChannels = request.initImageChannels;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Fallback to path
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!request.maskImageData.empty()) {
|
|
|
|
|
+ std::vector<uint8_t> binaryData(request.maskImageData.begin(), request.maskImageData.end());
|
|
|
|
|
+ jobInfo.maskImageData = Utils::base64Encode(binaryData);
|
|
|
|
|
+ jobInfo.maskImageWidth = request.maskImageWidth;
|
|
|
|
|
+ jobInfo.maskImageHeight = request.maskImageHeight;
|
|
|
|
|
+ jobInfo.maskImageChannels = request.maskImageChannels;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jobInfo.maskImageData = request.maskImagePath; // Fallback to path
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Model paths
|
|
|
|
|
- jobInfo.clipLPath = request.clipLPath;
|
|
|
|
|
- jobInfo.clipGPath = request.clipGPath;
|
|
|
|
|
- jobInfo.vaePath = request.vaePath;
|
|
|
|
|
- jobInfo.taesdPath = request.taesdPath;
|
|
|
|
|
- jobInfo.controlNetPath = request.controlNetPath;
|
|
|
|
|
- jobInfo.embeddingDir = request.embeddingDir;
|
|
|
|
|
- jobInfo.loraModelDir = request.loraModelDir;
|
|
|
|
|
- jobInfo.esrganPath = request.esrganPath;
|
|
|
|
|
- jobInfo.upscaleFactor = request.upscaleFactor;
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
GenerationQueue::GenerationQueue(ModelManager* modelManager, int maxConcurrentGenerations, const std::string& queueDir, const std::string& outputDir)
|
|
GenerationQueue::GenerationQueue(ModelManager* modelManager, int maxConcurrentGenerations, const std::string& queueDir, const std::string& outputDir)
|
|
|
: pImpl(std::make_unique<Impl>()) {
|
|
: pImpl(std::make_unique<Impl>()) {
|
|
@@ -1419,6 +1458,138 @@ GenerationQueue::GenerationQueue(ModelManager* modelManager, int maxConcurrentGe
|
|
|
|
|
|
|
|
// Load any existing jobs from disk
|
|
// Load any existing jobs from disk
|
|
|
pImpl->loadJobsFromDisk();
|
|
pImpl->loadJobsFromDisk();
|
|
|
|
|
+
|
|
|
|
|
+ // Process any jobs that were loaded from disk and are in PROCESSING status
|
|
|
|
|
+ // These jobs need to be converted from JobInfo back to GenerationRequest
|
|
|
|
|
+ std::vector<std::string> processingJobs;
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(pImpl->jobsMutex);
|
|
|
|
|
+ for (const auto& [jobId, jobInfo] : pImpl->activeJobs) {
|
|
|
|
|
+ if (jobInfo.status == GenerationStatus::PROCESSING) {
|
|
|
|
|
+ processingJobs.push_back(jobId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Convert processing jobs back to GenerationRequest and process them
|
|
|
|
|
+ for (const std::string& jobId : processingJobs) {
|
|
|
|
|
+ auto it = pImpl->activeJobs.find(jobId);
|
|
|
|
|
+ if (it != pImpl->activeJobs.end()) {
|
|
|
|
|
+ const JobInfo& jobInfo = it->second;
|
|
|
|
|
+
|
|
|
|
|
+ // Create GenerationRequest from JobInfo
|
|
|
|
|
+ GenerationRequest request;
|
|
|
|
|
+ request.id = jobInfo.id;
|
|
|
|
|
+ request.modelName = jobInfo.modelName;
|
|
|
|
|
+ request.prompt = jobInfo.prompt;
|
|
|
|
|
+ request.negativePrompt = jobInfo.negativePrompt;
|
|
|
|
|
+ request.width = jobInfo.width;
|
|
|
|
|
+ request.height = jobInfo.height;
|
|
|
|
|
+ request.batchCount = jobInfo.batchCount;
|
|
|
|
|
+ request.steps = jobInfo.steps;
|
|
|
|
|
+ request.cfgScale = jobInfo.cfgScale;
|
|
|
|
|
+ request.samplingMethod = jobInfo.samplingMethod;
|
|
|
|
|
+ request.scheduler = jobInfo.scheduler;
|
|
|
|
|
+ request.seed = jobInfo.seed;
|
|
|
|
|
+ request.strength = jobInfo.strength;
|
|
|
|
|
+ request.controlStrength = jobInfo.controlStrength;
|
|
|
|
|
+ request.clipSkip = jobInfo.clipSkip;
|
|
|
|
|
+ request.nThreads = jobInfo.nThreads;
|
|
|
|
|
+ request.offloadParamsToCpu = jobInfo.offloadParamsToCpu;
|
|
|
|
|
+ request.clipOnCpu = jobInfo.clipOnCpu;
|
|
|
|
|
+ request.vaeOnCpu = jobInfo.vaeOnCpu;
|
|
|
|
|
+ request.diffusionFlashAttn = jobInfo.diffusionFlashAttn;
|
|
|
|
|
+ request.diffusionConvDirect = jobInfo.diffusionConvDirect;
|
|
|
|
|
+ request.vaeConvDirect = jobInfo.vaeConvDirect;
|
|
|
|
|
+
|
|
|
|
|
+ // Set request type based on jobInfo.requestType
|
|
|
|
|
+ if (jobInfo.requestType == "text2img") {
|
|
|
|
|
+ request.requestType = GenerationRequest::RequestType::TEXT2IMG;
|
|
|
|
|
+ } else if (jobInfo.requestType == "img2img") {
|
|
|
|
|
+ request.requestType = GenerationRequest::RequestType::IMG2IMG;
|
|
|
|
|
+ // Convert base64 image data back to binary
|
|
|
|
|
+ if (!jobInfo.initImageData.empty()) {
|
|
|
|
|
+ std::string base64Data = jobInfo.initImageData;
|
|
|
|
|
+ // Remove data URI prefix if present
|
|
|
|
|
+ size_t commaPos = base64Data.find(',');
|
|
|
|
|
+ if (commaPos != std::string::npos) {
|
|
|
|
|
+ base64Data = base64Data.substr(commaPos + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ request.initImageData = Utils::base64Decode(base64Data);
|
|
|
|
|
+ request.initImageWidth = jobInfo.initImageWidth;
|
|
|
|
|
+ request.initImageHeight = jobInfo.initImageHeight;
|
|
|
|
|
+ request.initImageChannels = jobInfo.initImageChannels;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (jobInfo.requestType == "controlnet") {
|
|
|
|
|
+ request.requestType = GenerationRequest::RequestType::CONTROLNET;
|
|
|
|
|
+ // Convert base64 image data back to binary
|
|
|
|
|
+ if (!jobInfo.controlImageData.empty()) {
|
|
|
|
|
+ std::string base64Data = jobInfo.controlImageData;
|
|
|
|
|
+ size_t commaPos = base64Data.find(',');
|
|
|
|
|
+ if (commaPos != std::string::npos) {
|
|
|
|
|
+ base64Data = base64Data.substr(commaPos + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ request.controlImageData = Utils::base64Decode(base64Data);
|
|
|
|
|
+ request.controlImageWidth = jobInfo.controlImageWidth;
|
|
|
|
|
+ request.controlImageHeight = jobInfo.controlImageHeight;
|
|
|
|
|
+ request.controlImageChannels = jobInfo.controlImageChannels;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (jobInfo.requestType == "upscaler") {
|
|
|
|
|
+ request.requestType = GenerationRequest::RequestType::UPSCALER;
|
|
|
|
|
+ // Convert base64 image data back to binary
|
|
|
|
|
+ if (!jobInfo.initImageData.empty()) {
|
|
|
|
|
+ std::string base64Data = jobInfo.initImageData;
|
|
|
|
|
+ size_t commaPos = base64Data.find(',');
|
|
|
|
|
+ if (commaPos != std::string::npos) {
|
|
|
|
|
+ base64Data = base64Data.substr(commaPos + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ request.initImageData = Utils::base64Decode(base64Data);
|
|
|
|
|
+ request.initImageWidth = jobInfo.initImageWidth;
|
|
|
|
|
+ request.initImageHeight = jobInfo.initImageHeight;
|
|
|
|
|
+ request.initImageChannels = jobInfo.initImageChannels;
|
|
|
|
|
+ }
|
|
|
|
|
+ request.esrganPath = jobInfo.esrganPath;
|
|
|
|
|
+ request.upscaleFactor = jobInfo.upscaleFactor;
|
|
|
|
|
+ } else if (jobInfo.requestType == "inpainting") {
|
|
|
|
|
+ request.requestType = GenerationRequest::RequestType::INPAINTING;
|
|
|
|
|
+ // Convert base64 image data back to binary
|
|
|
|
|
+ if (!jobInfo.initImageData.empty()) {
|
|
|
|
|
+ std::string base64Data = jobInfo.initImageData;
|
|
|
|
|
+ size_t commaPos = base64Data.find(',');
|
|
|
|
|
+ if (commaPos != std::string::npos) {
|
|
|
|
|
+ base64Data = base64Data.substr(commaPos + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ request.initImageData = Utils::base64Decode(base64Data);
|
|
|
|
|
+ request.initImageWidth = jobInfo.initImageWidth;
|
|
|
|
|
+ request.initImageHeight = jobInfo.initImageHeight;
|
|
|
|
|
+ request.initImageChannels = jobInfo.initImageChannels;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!jobInfo.maskImageData.empty()) {
|
|
|
|
|
+ std::string base64Data = jobInfo.maskImageData;
|
|
|
|
|
+ size_t commaPos = base64Data.find(',');
|
|
|
|
|
+ if (commaPos != std::string::npos) {
|
|
|
|
|
+ base64Data = base64Data.substr(commaPos + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ request.maskImageData = Utils::base64Decode(base64Data);
|
|
|
|
|
+ request.maskImageWidth = jobInfo.maskImageWidth;
|
|
|
|
|
+ request.maskImageHeight = jobInfo.maskImageHeight;
|
|
|
|
|
+ request.maskImageChannels = jobInfo.maskImageChannels;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set model paths
|
|
|
|
|
+ request.clipLPath = jobInfo.clipLPath;
|
|
|
|
|
+ request.clipGPath = jobInfo.clipGPath;
|
|
|
|
|
+ request.vaePath = jobInfo.vaePath;
|
|
|
|
|
+ request.taesdPath = jobInfo.taesdPath;
|
|
|
|
|
+ request.controlNetPath = jobInfo.controlNetPath;
|
|
|
|
|
+ request.embeddingDir = jobInfo.embeddingDir;
|
|
|
|
|
+ request.loraModelDir = jobInfo.loraModelDir;
|
|
|
|
|
+
|
|
|
|
|
+ LOG_INFO("Resuming processing job from disk: " + jobId);
|
|
|
|
|
+ pImpl->processRequest(request);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
GenerationQueue::~GenerationQueue() {
|
|
GenerationQueue::~GenerationQueue() {
|