1

Automating Blog Post Headers with Firefly Services

 1 month ago
source link: https://www.raymondcamden.com/2024/03/27/automating-blog-post-headers-with-firefly-services
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
March 27, 2024

Automating Blog Post Headers with Firefly Services

Automating Blog Post Headers with Firefly Services

Yesterday I introduced you to Adobe's new offering, Firefly Services, and demonstrated a simple example of how to generate images from prompt using the REST APIs. Today I thought I'd share one of the little demos I've made with the API, and one specifically built to help out with my blog - generating headers.

My usual process for headers is to go to the Firefly website, enter a prompt, let it load, and then promptly change it to landscape and re-generate my prompt again. I always feel bad that the initial, square, images are essentially trashed. It occurred to me I could build a Node.js utility to generate the images at the exact right size and even quickly display them. Here's how I did it.

First, I designed the CLI so I can simply pass in a prompt. Here's how I handled that:

if(process.argv.length < 3) {
	console.log(styleText('red', 'Usage: makeheader.js <<prompt>>'));
	process.exit(1);
} 

const prompt = process.argv[2];

console.log(styleText('green', `Generating headers for: ${prompt}`));

Next, I authenticate, and create my images:

let token = await getFFAccessToken(FF_CLIENT_ID, FF_CLIENT_SECRET);
let result = await textToImage(prompt, FF_CLIENT_ID, token);

I showed both of these methods yesterday, but my parameters for the Firefly API to generate images are slightly tweaked though. First, the authentication method again:

async function getFFAccessToken(id, secret) {

	const params = new URLSearchParams();

	params.append('grant_type', 'client_credentials');
	params.append('client_id', id);
	params.append('client_secret', secret);
	params.append('scope', 'openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis');
	
	let resp = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', 
		{ 
			method: 'POST', 
			body: params
		}
	);

	let data = await resp.json();
	return data.access_token;
}

And here's the call to the text to image API:

async function textToImage(text, id, token) {

	let body = {
		"n":4,
		"prompt":text,
		"size":{
			"width":"2304",
			"height":"1792"
		}
	}

	let req = await fetch('https://firefly-api.adobe.io/v2/images/generate', {
		method:'POST',
		headers: {
			'X-Api-Key':id, 
			'Authorization':`Bearer ${token}`,
			'Content-Type':'application/json'
		}, 
		body: JSON.stringify(body)
	});

	let resp = await req.json();
	return resp;
}

Note two things here:

  • First, I set n to 4 so I get 4 results, not the default of 1.
  • My size is hard coded to the landscape size.

Ok, so that's the easy bit honestly. But I wanted to do something cool with the results. There is a really useful npm package called open that will open URLs and files. The result of the Firefly API call above will include 4 URLs and I could have simply opened all four of them in individual browser tabs, but I wanted one page where I could see them all, much like the Firefly website. While not directly supported by open yet, I got around it by generating a temporary HTML file locally:

let html = `
<style>
img {
	max-width: 650px;
}

.results {
	display: grid;
	grid-template-columns: repeat(2, 50%);
}
</style>
<h2>Results for Prompt: ${prompt}</h2>
<div class="results">
`;

result.outputs.forEach(i => {
	html += `<p><img src="${i.image.presignedUrl}"></p>`;
});

html += '</div>';

let filename = `${uuid4()}.html`;
fs.writeFileSync(filename, html, 'utf8');
await open(filename, {
	wait: true
});
fs.unlinkSync(filename);

So now what happens is, I run my prompt, and when it's done, I get an HTML page. Here's the result of using:

node makeheader "a somber, moody picture of a cat in painters clothes, standing before an easel, thinking about what to pain
t next"
Sample output.

And yes, I used the fourth image for this post. Here's the complete script, but you can also find it in my Firefly API repo: https://github.com/cfjedimaster/fireflyapi/tree/main/demos/makeheader

// Requires Node 21.7.0
process.loadEnvFile();
import { styleText } from 'node:util';
import { v4 as uuid4 } from 'uuid';
import open from 'open';
import fs from 'fs';

const FF_CLIENT_ID = process.env.FF_CLIENT_ID;
const FF_CLIENT_SECRET = process.env.FF_CLIENT_SECRET;

async function getFFAccessToken(id, secret) {

	const params = new URLSearchParams();

	params.append('grant_type', 'client_credentials');
	params.append('client_id', id);
	params.append('client_secret', secret);
	params.append('scope', 'openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis');
	
	let resp = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', 
		{ 
			method: 'POST', 
			body: params
		}
	);

	let data = await resp.json();
	return data.access_token;
}

async function textToImage(text, id, token) {

	let body = {
		"n":4,
		"prompt":text,
		"size":{
			"width":"2304",
			"height":"1792"
		}
	}

	let req = await fetch('https://firefly-api.adobe.io/v2/images/generate', {
		method:'POST',
		headers: {
			'X-Api-Key':id, 
			'Authorization':`Bearer ${token}`,
			'Content-Type':'application/json'
		}, 
		body: JSON.stringify(body)
	});

	let resp = await req.json();
	return resp;
}


if(process.argv.length < 3) {
	console.log(styleText('red', 'Usage: makeheader.js <<prompt>>'));
	process.exit(1);
} 

const prompt = process.argv[2];

console.log(styleText('green', `Generating headers for: ${prompt}`));

let token = await getFFAccessToken(FF_CLIENT_ID, FF_CLIENT_SECRET);
let result = await textToImage(prompt, FF_CLIENT_ID, token);

console.log(styleText('green', 'Results generated - creating preview...'));

let html = `
<style>
img {
	max-width: 650px;
}

.results {
	display: grid;
	grid-template-columns: repeat(2, 50%);
}
</style>
<h2>Results for Prompt: ${prompt}</h2>
<div class="results">
`;

result.outputs.forEach(i => {
	html += `<p><img src="${i.image.presignedUrl}"></p>`;
});

html += '</div>';

let filename = `${uuid4()}.html`;
fs.writeFileSync(filename, html, 'utf8');
await open(filename, {
	wait: true
});
fs.unlinkSync(filename);

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK