Last active
May 14, 2023 12:25
-
-
Save shinshin86/b0313c37e4c13ef97e0c4ac12c547427 to your computer and use it in GitHub Desktop.
Sample Node.js to read metadata of PNG files created with Stable Diffusion web UI.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Sample Node.js to read metadata of PNG files created with Stable Diffusion Web UI | |
* | |
* Code from the following page is used as a reference | |
* URL: https://qiita.com/javacommons/items/472e85be1b11098172b3 | |
*/ | |
const fs = require('fs').promises; | |
async function getPngInfo(fileName) { | |
const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); | |
const crcSize = 4; | |
let result = ""; | |
const buffer = await fs.readFile(fileName); | |
const realSig = buffer.slice(0, 8); | |
if (!realSig.equals(signature)) { | |
return ""; | |
} | |
let position = 8; | |
while (position < buffer.length) { | |
const length = buffer.readUInt32BE(position); | |
position += 4; | |
const chunkType = buffer.slice(position, position + 4).toString(); | |
position += 4; | |
if (chunkType === 'tEXt' || chunkType === 'iTXt') { | |
const s = buffer.slice(position, position + length); | |
position += length; | |
if (s.slice(0, 10).toString() === 'parameters') { | |
let sRest = s.slice(10); | |
while (sRest.length > 0 && sRest[0] === 0) { | |
sRest = sRest.slice(1); | |
} | |
result = chunkType === 'tEXt' ? sRest.toString('latin1') : sRest.toString('utf8'); | |
break; | |
} | |
} else { | |
position += length; | |
} | |
position += crcSize; | |
} | |
return result; | |
} | |
async function getPngInfoJson(fileName) { | |
const infoString = await getPngInfo(fileName); | |
const lines = infoString.split("\n"); | |
let phase = 0; | |
let prompt = ""; | |
let negativePrompt = ""; | |
const data = {}; | |
for (const line of lines) { | |
const re = /([A-Z][a-zA-Z0-9 ]*: [^,]+)(, )?/g; | |
if (phase === 0) { | |
if (line.startsWith("Negative prompt: ")) { | |
negativePrompt = line.slice(17); | |
phase = 1; | |
continue; | |
} | |
const m = re.exec(line); | |
if (m) { | |
phase = 2; | |
} else { | |
prompt += "\n" + line; | |
continue; | |
} | |
} | |
if (phase === 1) { | |
const m = re.exec(line); | |
if (!m) { | |
negativePrompt += "\n" + line; | |
continue; | |
} | |
phase = 2; | |
} | |
if (phase === 2) { | |
let match1; | |
while ((match1 = re.exec(line)) !== null) { | |
const [_, word] = match1; | |
const rx1 = /([A-Z][a-zA-Z0-9 ]*): ([^,]+)(, )?/g; | |
let match2; | |
while ((match2 = rx1.exec(word)) !== null) { | |
const [_, name, value] = match2; | |
data[name] = value; | |
} | |
} | |
} | |
} | |
data["Prompt"] = prompt.trim(); | |
data["Negative prompt"] = negativePrompt.trim(); | |
return JSON.stringify(data); | |
} | |
(async () => { | |
const args = process.argv.slice(2); | |
if (args.length !== 1) { | |
console.error("Usage: node app.js <filename>"); | |
process.exit(1); | |
} | |
const fileName = args[0]; | |
try { | |
const result = await getPngInfo(fileName); | |
console.log('===PNG INFO===') | |
console.log(result); | |
const json = await getPngInfoJson(fileName); | |
console.log('===PNG INFO (JSON)===') | |
console.log(json); | |
} catch (err) { | |
console.error(`Error reading file ${fileName}: ${err.message}`); | |
process.exit(1); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment