File size: 8,170 Bytes
eee16bc
 
 
 
 
 
 
 
 
 
50acb31
a6f672d
 
eee16bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { createCanvas, loadImage } = require('canvas');
const { HfInference } = require("@huggingface/inference");

require("dotenv").config()

// Cache directory to store generated images
const CACHE_DIR = `./public/image_out`


if (!fs.existsSync(CACHE_DIR)) {
  fs.mkdirSync(CACHE_DIR);
}
const available_models = ["alimama-creative/FLUX.1-Turbo-Alpha", "black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-schnell", "CompVis/stable-diffusion-v1-4", "Corcelio/mobius", "digiplay/AnalogMadness-realistic-model-v7", "digiplay/AsianBrmBeautyrealmix_v2.0", "digiplay/DonutHoleMix_Beta", "digiplay/KawaiiRealisticAsian_v0.7", "digiplay/m0nst3rfy3-testfix", "digiplay/m3u", "digiplay/MilkyWonderland_v1", "digiplay/Realisian_v6", "digiplay/realmixUnrealjourney_v1", "digiplay/STRANGER", "digiplay/STRANGER-ANIME", "dreamlike-art/dreamlike-diffusion-1.0", "dreamlike-art/dreamlike-photoreal-2.0", "fluently/Fluently-XL-v2", "GraydientPlatformAPI/yamers-nsfw4-xl", "John6666/3x3x3mixxl-v2-sdxl", "John6666/4th-tail-merges-050tponynai-sdxl", "John6666/4th-tail-merges-050wai70-sdxl", "John6666/big-lust-v1-sdxl", "John6666/carnival-unchained-v10-fp8-flux", "John6666/convtest", "John6666/convtest2", "John6666/dgs-4th-darkness-03ad-sdxl", "John6666/digital-af-xlp-v1-sdxl", "John6666/josei-realistic-v11-sdxl", "John6666/lyh-anime-flux-v2a1-fp8-flux", "John6666/mala-anime-mix-nsfw-pony-xl-v5-sdxl-spo", "John6666/mala-anime-mix-nsfw-pony-xl-v5new-sdxl", "John6666/mala-anime-mix-nsfw-pony-xl-v5new-sdxl-spo", "John6666/mala-smooth-v1-sdxl", "John6666/mikoshi-pony-v1-sdxl", "John6666/mklan-aio-nsfw-aio-nextgen-xlv2-sdxl", "John6666/nova-reality-v50-sdxl", "John6666/optimal-criminal-pony-v10-sdxl", "John6666/photo-realistic-pony-v5-sdxl", "John6666/pornworks-real-porn-v03-sdxl", "John6666/pornworks-real-porn-v04-sdxl", "John6666/real-mix-pony-v01-sdxl", "John6666/real-mix-pony-v2-sdxl", "John6666/reasianpony-merge-v10-sdxl", "John6666/relh-checkpoint-v30-sdxl", "John6666/sakuramoon-v10-sdxl", "John6666/sapianf-nude-men-women-for-flux-v20fp16-fp8-flux", "John6666/sorkh-pony-v1-sdxl", "John6666/speciosa-anime-v14-sdxl", "John6666/st2-bedamnrealpony-v1-sdxl", "John6666/suimix-xl-v10-sdxl", "John6666/sumeshi-flux1s-v002e-fp8-flux", "John6666/titania-juggernaut-mix-v1-sdxl", "John6666/titania-mix-realistic-pony-gbv10-sdxl", "John6666/uber-realistic-porn-merge-xl-urpmxl-v6final-sdxl", "John6666/ultimate-realistic-mix-v1-sdxl", "John6666/unlimited-porn-x-sdxl", "John6666/unlimited-porn-xreal-sdxl", "John6666/wai-ani-hentai-pony-v5-sdxl", "John6666/wai-c-v3-sdxl", "John6666/wai-doll-cn-v2-sdxl", "John6666/wai-simpleuse-for-real-pony-v1-sdxl", "Niggendar/duchaitenPonyXLNo_v35", "sd-community/sdxl-flash", "stabilityai/stable-diffusion-2-1", "stabilityai/stable-diffusion-2-base", "stabilityai/stable-diffusion-3-medium-diffusers", "stabilityai/stable-diffusion-xl-base-1.0", "stable-diffusion-v1-5/stable-diffusion-v1-5", "stablediffusionapi/disney-pixar-cartoon", "stablediffusionapi/dreamshaper-v6", "stablediffusionapi/mklan-xxx-nsfw-pony", "stablediffusionapi/newrealityxl-global-nsfw", "UnfilteredAI/NSFW-gen-v2", "Yntec/3DKX", "Yntec/BetterPonyDiffusion", "Yntec/Chip_n_DallE", "Yntec/DramaLlama", "Yntec/DreamlikeShaper", "Yntec/DreamPhotoGASM", "Yntec/DreamWorld", "Yntec/DucHaitenGODofSIMP", "Yntec/ElldrethSDaydreamMix", "Yntec/epiCPhotoGasm", "Yntec/MeinaAlter", "Yntec/MGM", "Yntec/MostClassical", "Yntec/nuipenimix2", "Yntec/Ponygraphy", "Yntec/RevAnimatedV2Rebirth", "Yntec/SCMIX_NightSkyMeina", "Yntec/TrueSight", "Yntec/WoopWoopAnime", "Yntec/YiffyMix", "Yntec/ZootVisionEpsilon"]

// Hugging Face Inference API configuration
const HF_API_URL = 'https://api-inference.huggingface.co/models/';
const HF_API_TOKEN = process.env.HF_API_TOKEN || ''; // Replace with your actual API token
const inferenceClient = new HfInference(HF_API_TOKEN);


const DEFAULT_USER_API_KEY = 'PUBLIC'; // Default API key for demonstration purposes
//const DEFAULT_GUIDANCE_SCALE = 5.0; // Default guidance scale for the diffusion model
const DEFAULT_WIDTH = 1024; // Default width of the generated image
const DEFAULT_HEIGHT = 1024; // Default height of the generated image
// In-memory store for user credits (for demonstration purposes)
// In production, consider using a database
const userCredits = {
  'PUBLIC': 20000,
  'user_api_key_1': 100, // Example API key with 10 credits
  'user_api_key_2': 5   // Example API key with 5 credits
};

const generateImage = async(requestPayload, res, responseFormat) => {
  const prompt = requestPayload.prompt;
  const seed = requestPayload.seed ? parseInt(requestPayload.seed) : parseInt(Math.random() * 100000);
  const modelId = requestPayload.model || 'black-forest-labs/FLUX.1-schnell';
  const apiKey = requestPayload.api_key || DEFAULT_USER_API_KEY;
  const cfg_scale = requestPayload.guidance_scale ? parseFloat(requestPayload.guidance_scale) : 7.0;
  const steps = requestPayload.steps ? parseInt(requestPayload.steps) : 4;
  const width = requestPayload.width ? parseInt(requestPayload.width) : DEFAULT_WIDTH;
  const height = requestPayload.height ? parseInt(requestPayload.height) : DEFAULT_HEIGHT;

  if (!prompt) {
    return res.status(400).send('Error: No prompt provided');
  }

  if (!apiKey) {
    return res.status(400).send('Error: No API key provided');
  }

  // Check if the API key is valid and if the user has enough credits
  if (!userCredits.hasOwnProperty(apiKey)) {
    return res.status(403).send('Error: Invalid API key');
  }

  if (userCredits[apiKey] <= 0) {
    return res.status(403).send('Error: Insufficient credits');
  }

  // Set the seed if provided (for caching purposes, does affect HF API)
  const seedStr =  `_${seed}`

  // Create a hash of the prompt, seed, and modelId to use as the cache key
  const cacheKey = crypto.createHash('sha256').update(`${prompt}${seedStr}_${modelId}`).digest('hex');
  const cachePath = path.join(CACHE_DIR, `${cacheKey}.png`);
  console.log(cachePath)
  // Check if the image already exists in the cache
  if (fs.existsSync(cachePath)) {
    return res.sendFile(cachePath);
  }

  try {
    // Prepare payload for Hugging Face Inference API
    const payload = ({
        "inputs": prompt,
        "parameters": {"width": width, "height": height},
        "seed": seed
        //seed,
        //cfg_scale,
        //steps
    });
    console.log("Payload: ", JSON.stringify(payload, null, 2));
    console.log("Endpoint: ", `${HF_API_URL}${modelId}`);
    // Send request to Hugging Face Inference API to generate the image
    const response = await axios.post(
      `${HF_API_URL}${modelId}`,
      payload,
      {
        headers: {
          'Content-Type': 'application/json',
          authorization: `Bearer ${HF_API_TOKEN}`
        },
        responseType: 'arraybuffer'
      }
    );

    if (response.status !== 200) {
      return res.status(500).send(`Error: Failed to generate image, see response fuckup here: ${JSON.stringify(response)}`);  
    }

    // Deduct one credit from the user's balance
    userCredits[apiKey] -= 1;

    // Save the image to the cache directory
    fs.writeFileSync(cachePath, response.data);

    // Send either the image directly or a link to the image
    if (responseFormat == "url") {
      res.setHeader("Content-Type", "application/json")
      res.json({"image_url": "http://localhost:8000/image_cache/"+ cachePath.split("/").at(-1)})
      res.end()
    } else {
      res.setHeader('Content-Type', 'image/png');
      res.sendFile(cachePath);
    }
  } catch (error) {
    res.status(500).send(`Error: Failed to generate image - ${error.message}`);
  }

}
/*
app.get('/models', async(req, res) =>{
  res.send(available_models)
})

//for prompt-in-url: <img src="https://yourserver.com/generate/image?prompt=A%20large%20hamster&width=1024&height=1024"
app.get('/generate/image', async (req, res) => {
  await generateImage(req.query, res)
});

app.post("/generate", async(req, res)=> {
  await generateImage(req.body, res)
}) */
module.exports = {generateImage}