-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsparkimage.module
More file actions
306 lines (261 loc) · 10.6 KB
/
sparkimage.module
File metadata and controls
306 lines (261 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
<?php
/**
* @file
* Contains sparkimage.module.
*/
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\NodeInterface;
use Drupal\ai\OperationType\TextToImage\TextToImageInput;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_entity_presave().
*
* Set the alt text of the image field based on the alternative text field
* after AI automaters have run.
*/
function sparkimage_entity_presave(EntityInterface $entity) {
// Check if the entity is a node and of type 'image'
if ($entity instanceof NodeInterface && $entity->bundle() === 'image') {
// Check if the node has the required fields
if ($entity->hasField('field_generated_image') && $entity->hasField('field_image_alternative_text')) {
// Get the alternative text field value
$alt_text = $entity->get('field_image_alternative_text')->value;
// If we have alt text and the image field has a value
if (!empty($alt_text) && !$entity->get('field_generated_image')->isEmpty()) {
// Get the image field
$image_field = $entity->get('field_generated_image');
// Loop through all image field values (in case there are multiple)
foreach ($image_field as $delta => $item) {
// Set the alt text for the image field
$values = $item->getValue();
$values['alt'] = $alt_text;
$item->setValue($values);
}
}
}
}
}
/**
* Implements hook_ENTITY_TYPE_presave() for node entities.
*/
function sparkimage_node_presave(NodeInterface $node) {
// Only process Image nodes
if ($node->bundle() !== 'image') {
return;
}
// Store the image style setting for AI Image Generation
if ($node->hasField('field_gen_image_style') && !$node->get('field_gen_image_style')->isEmpty()) {
$style = $node->get('field_gen_image_style')->value;
if (!empty($style)) {
\Drupal::state()->set('sparkimage.image_style', $style);
}
}
// Check if this is a request to generate a new image
if ($node->hasField('field_image_prompt') && !$node->get('field_image_prompt')->isEmpty()) {
// Get the prompt from field_image_prompt
$prompt = $node->get('field_image_prompt')->value;
// If there's a prompt, store it for potential use in later hooks
if (!empty($prompt)) {
\Drupal::state()->set('sparkimage.image_prompt', $prompt);
// Check if we should generate an image now
// Only generate if the field_generated_image is empty or if explicitly requested
if ($node->hasField('field_generated_image') &&
($node->get('field_generated_image')->isEmpty() ||
$node->isNew() ||
isset($node->sparkimage_generate_image))) {
// Generate the image using our helper function
sparkimage_generate_ai_image($node);
}
}
}
}
/**
* Generate an AI image using the node's prompt and style fields.
*
* @param \Drupal\node\NodeInterface $node
* The node entity containing the prompt and style information.
*
* @return bool
* TRUE if image generation was successful, FALSE otherwise.
*/
function sparkimage_generate_ai_image(NodeInterface $node) {
// Verify we have a prompt
if (!$node->hasField('field_image_prompt') || $node->get('field_image_prompt')->isEmpty()) {
\Drupal::messenger()->addError(t('Cannot generate an image without a prompt.'));
return FALSE;
}
// Get the prompt from field_image_prompt
$prompt = $node->get('field_image_prompt')->value;
// Get the style if available
$style = 'vivid'; // Default style
if ($node->hasField('field_gen_image_style') && !$node->get('field_gen_image_style')->isEmpty()) {
$style = $node->get('field_gen_image_style')->value;
}
try {
// Set up the AI provider
$provider = \Drupal::service('ai.provider')->createInstance('openai');
// Create configuration with our style setting
$config = [
'n' => 1,
'response_format' => 'url',
'size' => '1024x1024',
'quality' => 'standard',
'style' => $style,
];
// Set the configuration
$provider->setConfiguration($config);
// Create input with the prompt from field_image_prompt
$input = new TextToImageInput($prompt);
// Generate the image
$textToImageOutput = $provider->textToImage($input, 'dall-e-3', ['sparkimage']);
$response = $textToImageOutput->getNormalized();
// If we have an image, save it and set it on the field
if (!empty($response) && isset($response[0])) {
$file = $response[0]->getAsFileEntity('public://', 'ai_image_' . time() . '.png');
if ($file) {
$file->setPermanent();
$file->save();
// Generate alt text for the image using our service
$alt_text = '';
$file_uri = $file->getFileUri();
$file_path = \Drupal::service('file_system')->realpath($file_uri);
\Drupal::logger('sparkimage')->notice('Generated image on node @nid: @path', [
'@nid' => $node->id(),
'@path' => $file_path,
]);
// Use our ImageAltTextGenerator service to generate alt text
$alt_text_generator = \Drupal::service('sparkimage.alt_text_generator');
$generated_alt_text = $alt_text_generator->generateAltText($file_path, 'openai', 'gpt-4o-mini');
// If we got alt text, save it to the field_image_alternative_text field
if (!empty($generated_alt_text)) {
$alt_text = $generated_alt_text;
// Save the alt text to the field if it exists
if ($node->hasField('field_image_alternative_text')) {
$node->set('field_image_alternative_text', $alt_text);
\Drupal::logger('sparkimage')->notice('Generated alt text for image on node @nid: @alt', [
'@nid' => $node->id(),
'@alt' => $alt_text,
]);
}
}
// Set the file on the field_generated_image
$image_field = $node->get('field_generated_image');
$values = [
'target_id' => $file->id(),
'alt' => $alt_text,
];
$image_field->setValue($values);
// Check if the response contains revised_prompt in the raw output
$raw_output = $textToImageOutput->getRawOutput();
if (is_array($raw_output) && isset($raw_output['data'][0]['revised_prompt'])) {
$revised_prompt = $raw_output['data'][0]['revised_prompt'];
$node->set('field_image_prompt', $revised_prompt);
\Drupal::logger('sparkimage')->notice('Updated image prompt to revised prompt for node @nid', [
'@nid' => $node->id(),
]);
}
// Log success
\Drupal::logger('sparkimage')->notice('Successfully generated AI image for node @nid using prompt: @prompt', [
'@nid' => $node->id(),
'@prompt' => $prompt,
]);
return TRUE;
}
}
}
catch (\Exception $e) {
\Drupal::messenger()->addError(t('Error generating image: @message', ['@message' => $e->getMessage()]));
\Drupal::logger('sparkimage')->error('Error generating AI image: @message', ['@message' => $e->getMessage()]);
}
return FALSE;
}
/**
* Implements hook_form_alter().
*/
function sparkimage_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Check if this is an Image node form
if (in_array($form_id, ['node_image_form', 'node_image_edit_form'])) {
// Add a custom submit handler to generate the image
$form['actions']['submit']['#submit'][] = 'sparkimage_node_form_submit';
// Check if this is the regenerate form display mode
$request = \Drupal::request();
if ($request->query->get('display') === 'regenerate') {
// Add a "Save and Regenerate" button
$form['actions']['save_regenerate'] = [
'#type' => 'submit',
'#value' => t('Save and Regenerate'),
'#submit' => ['sparkimage_save_regenerate_submit'],
'#weight' => 0,
'#button_type' => 'primary',
'#access' => TRUE,
];
// Remove all other buttons
if (isset($form['actions']['submit'])) {
$form['actions']['submit']['#access'] = FALSE;
}
if (isset($form['actions']['delete'])) {
$form['actions']['delete']['#access'] = FALSE;
}
if (isset($form['actions']['generate_image'])) {
$form['actions']['generate_image']['#access'] = FALSE;
}
}
// Add a custom button to regenerate the image even if one already exists
if ($request->query->get('display') !== 'regenerate') {
$form['actions']['generate_image'] = [
'#type' => 'submit',
'#value' => t('Generate AI Image'),
'#submit' => ['sparkimage_generate_image_submit'],
'#weight' => 5,
'#access' => TRUE,
];
}
}
}
/**
* Custom submit handler for the Image node form.
*/
function sparkimage_node_form_submit($form, FormStateInterface $form_state) {
// Nothing special needed here, the node_presave hook will handle image generation
// if field_generated_image is empty or the node is new
}
/**
* Submit handler for the "Generate AI Image" button.
*/
function sparkimage_generate_image_submit($form, FormStateInterface $form_state) {
// Get the node entity
$entity = $form_state->getFormObject()->getEntity();
// Set a flag to force image generation even if the image field isn't empty
$entity->sparkimage_generate_image = TRUE;
// Add a message to inform the user
\Drupal::messenger()->addStatus(t('Generating AI image...'));
}
/**
* Submit handler for the "Save and Regenerate" button.
*/
function sparkimage_save_regenerate_submit($form, FormStateInterface $form_state) {
// Get the node entity
$entity = $form_state->getFormObject()->getEntity();
// Ensure the entity is saved with the form values
$entity = $form_state->getFormObject()->buildEntity($form, $form_state);
// Set a flag to force image generation even if the image field isn't empty
$entity->sparkimage_generate_image = TRUE;
// Save the entity with the updated values
$entity->save();
// Add a message to inform the user
\Drupal::messenger()->addStatus(t('Regenerated AI image.'));
// Redirect to the canonical view of the node
$form_state->setRedirect('entity.node.canonical', ['node' => $entity->id()]);
}
/**
* Implements hook_preprocess_HOOK() for field templates.
*/
function sparkimage_preprocess_field(&$variables) {
// Add a custom class to the image field to help with custom styling
$element = $variables['element'];
if ($element['#entity_type'] == 'node' &&
$element['#bundle'] == 'image' &&
$element['#field_name'] == 'field_generated_image') {
$variables['attributes']['class'][] = 'ai-generated-image';
}
}