├── app ├── page.module.css ├── favicon.ico ├── assets │ └── c2.jpg ├── page.js ├── globals.css ├── layout.js ├── intro.js ├── navbar.js └── dashboard │ └── page.js ├── jsconfig.json ├── server ├── pdfs │ └── newFileName613.pdf ├── index.js └── utils │ └── Controller.js ├── postcss.config.js ├── .env.example ├── next.config.mjs ├── tailwind.config.js ├── .gitignore ├── public ├── vercel.svg └── next.svg ├── package.json └── README.md /app/page.module.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Girishbari/comic-cult/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /app/assets/c2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Girishbari/comic-cult/HEAD/app/assets/c2.jpg -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/pdfs/newFileName613.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Girishbari/comic-cult/HEAD/server/pdfs/newFileName613.pdf -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | geminiAI="" 2 | 3 | 4 | STABLE_DIFFUSION_KEY="" 5 | // possibly set it in flagsmith dashboard 6 | 7 | user="" 8 | pass="" 9 | flagsmith_key="" -------------------------------------------------------------------------------- /app/page.js: -------------------------------------------------------------------------------- 1 | 2 | import Intro from "./intro"; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | *, 6 | ::before, 7 | ::after { 8 | margin: 0; 9 | padding: 0; 10 | box-sizing: border-box; 11 | } 12 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | 4 | env: { 5 | flagsmith_key: process.env.flagsmith_key 6 | } 7 | }; 8 | 9 | export default nextConfig; 10 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | }, 11 | plugins: [require("daisyui")], 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | #env variable 10 | ./env 11 | 12 | # testing 13 | /coverage 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | import { Bangers } from "next/font/google"; 2 | import "./globals.css"; 3 | import Navbar from "./navbar"; 4 | 5 | const play = Bangers({ 6 | weight: ["400"], 7 | style: ["normal"], 8 | subsets: ["latin"], 9 | display: "swap", 10 | }); 11 | 12 | export const metadata = { 13 | title: "Comic-cult transform your Ideas ", 14 | description: 15 | "ever thought of bringing your imagination into real I am talking", 16 | }; 17 | 18 | export default function RootLayout({ children }) { 19 | return ( 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | {children} 28 |
29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "server": "node server/index.js" 11 | }, 12 | "dependencies": { 13 | "@google/generative-ai": "^0.3.1", 14 | "canvas": "^2.11.2", 15 | "cors": "^2.8.5", 16 | "dotenv": "^16.4.5", 17 | "express": "^4.18.3", 18 | "flagsmith-nodejs": "^3.2.0", 19 | "flagsmith": "^3.24.0", 20 | "next": "14.1.3", 21 | "nodemailer": "^6.9.13", 22 | "pdfkit": "^0.15.0", 23 | "react": "^18", 24 | "react-dom": "^18" 25 | }, 26 | "description": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).", 27 | "main": "index.js", 28 | "keywords": [], 29 | "author": "", 30 | "license": "ISC", 31 | "devDependencies": { 32 | "autoprefixer": "^10.4.19", 33 | "daisyui": "^4.9.0", 34 | "postcss": "^8.4.38", 35 | "tailwindcss": "^3.4.3" 36 | } 37 | } -------------------------------------------------------------------------------- /app/intro.js: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { useRouter } from 'next/navigation' 3 | 4 | function intro() { 5 | 6 | const router = useRouter(); 7 | return ( 8 | <> 9 |
10 |
11 |

15 | Comic-cult 16 |

17 |

18 | "redbull gives you wings we give a comic"
19 | provide us your story we'll generate amazing art just like you 20 | see any comics
21 | See Examples from Navigation bar 22 |

23 | 25 |
26 |
27 | 28 | ); 29 | } 30 | 31 | export default intro; 32 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/navbar.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | 3 | export default function navbar() { 4 | return ( 5 |
6 |
7 | Comic-Cult 8 |
9 |
10 | examples 11 | How to use ? 12 | Get Started 13 | 14 |
15 | {/* Mobile navbar */} 16 |
17 |
18 | 21 | 31 |
32 |
33 |
34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const cors = require("cors"); 3 | const { textToComic } = require("./utils/Controller.js"); 4 | const path = require("path"); 5 | const PDFDocument = require("pdfkit"); 6 | const fs = require("fs"); 7 | var nodemailer = require('nodemailer'); 8 | require('dotenv').config(); 9 | 10 | 11 | 12 | var transporter = nodemailer.createTransport({ 13 | service: 'gmail', 14 | auth: { 15 | user: 'barigirish21@gmail.com', 16 | pass: 'wjta teuw ljoq taxn' 17 | } 18 | }) 19 | 20 | const app = express(); 21 | app.use( 22 | cors({ 23 | origin: "http://localhost:3000", 24 | }) 25 | ); 26 | 27 | const PORT = 5000; 28 | 29 | app.use(express.json()); 30 | app.use(express.urlencoded({ extended: true })); 31 | 32 | const makePdf = () => { 33 | return new Promise((resolve, reject) => { 34 | try { 35 | const imageFiles = fs.readdirSync("./final/main/"); 36 | if (imageFiles.length === 0) { 37 | console.log(imageFiles) 38 | reject("no image has been generated") 39 | return; 40 | }; 41 | let randomNum = Math.floor(Math.random() * 1000); 42 | 43 | const doc = new PDFDocument({ size: [512, 515] }); 44 | const pdfPath = path.join(__dirname, '/pdfs/', `newFileName${randomNum}.pdf`); 45 | 46 | console.log("yes from makePDF"); 47 | if (!pdfPath) reject("no path generated"); 48 | doc.pipe(fs.createWriteStream(pdfPath)); 49 | 50 | imageFiles.forEach((image) => { 51 | const imagePath = `./final/main/${image}`; 52 | doc.image(imagePath, 1, 1, { fit: [512, 512] }); 53 | doc.addPage(); 54 | }); 55 | doc.end(); 56 | resolve(pdfPath); 57 | } catch (error) { 58 | reject(error); 59 | } 60 | }); 61 | }; 62 | 63 | app.get("/download", async (req, res) => { 64 | const pdfPath = await makePdf(); 65 | var mailOptions = { 66 | from: 'barigirish21@gmail.com', 67 | to: 'girishbari15@gmail.com', 68 | subject: 'Sending Email using Node.js', 69 | text: 'That was easy!', 70 | attachments: [ 71 | { 72 | filename: 'example.pdf', 73 | content: fs.createReadStream(pdfPath) 74 | } 75 | ] 76 | }; 77 | transporter.sendMail(mailOptions, function (error, info) { 78 | if (error) { 79 | console.log(error); 80 | } else { 81 | console.log('Email sent: ' + info.response); 82 | } 83 | }); 84 | 85 | }); 86 | 87 | app.post("/", async (req, res) => { 88 | const { userText, customization, diffusionKey } = req.body; 89 | textToComic(userText, customization, diffusionKey) 90 | .then(() => { 91 | return makePdf(); 92 | }) 93 | .then((pdfPath) => { 94 | console.log("PDF has been created" + pdfPath); 95 | var mailOptions = { 96 | from: 'barigirish21@gmail.com', 97 | to: 'barigirish50@gmail.com', 98 | subject: 'Your Comic', 99 | text: 'That was easy!', 100 | attachments: [ 101 | { 102 | filename: 'example.pdf', 103 | content: fs.createReadStream(pdfPath) 104 | } 105 | ] 106 | }; 107 | transporter.sendMail(mailOptions, function (error, info) { 108 | if (error) { 109 | console.log(error); 110 | } else { 111 | console.log('Email sent: ' + info.response); 112 | res.status(200).json({ message: info.response }) 113 | } 114 | }); 115 | }) 116 | .catch((error) => { 117 | console.log("error has been created", error); 118 | res.status(404).json({ error: error.message }); 119 | }); 120 | }); 121 | 122 | app.listen(PORT, () => console.log(`App listening at localhost:${PORT}`)); 123 | -------------------------------------------------------------------------------- /app/dashboard/page.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useRef, useState } from "react"; 4 | import flagsmith from "flagsmith"; 5 | import Navbar from "../navbar" 6 | 7 | export default function form() { 8 | const [inputData, setInputData] = useState({ 9 | userText: "", 10 | customization: "", 11 | diffusionKey: "", 12 | }); 13 | const [flagState, setFlagState] = useState(); 14 | const [loading, setLoading] = useState(); 15 | const keyRef = useRef(); 16 | 17 | async function getFlags() { 18 | await flagsmith.init({ 19 | environmentID: process.env.flagsmith_key, 20 | onChange: (oldFlags, params) => { 21 | if (flagsmith.hasFeature("STABLE_DIFFUSION_KEY")) { 22 | keyRef.current.disabled = true; 23 | } else { 24 | keyRef.current.disabled = false; 25 | } 26 | }, 27 | }); 28 | } 29 | 30 | useState(() => { 31 | getFlags(); 32 | }, []); 33 | 34 | function handleChange(e) { 35 | const { name, value } = e.target; 36 | setInputData({ ...inputData, [name]: value }); 37 | } 38 | 39 | /* async function handleSubmit() { 40 | console.log(inputData); 41 | try { 42 | if (inputData.prompt === "" || inputData.customization === "") { 43 | alert("data is not filled"); 44 | return; 45 | } 46 | const response = await fetch("http://localhost:5000/", { 47 | method: "GET", 48 | headers: { 49 | "Content-Type": "application/json", 50 | }, 51 | }) 52 | if (response.ok) { 53 | const blob = await response.blob(); 54 | const url = window.URL.createObjectURL(new Blob([blob])); 55 | const link = document.createElement('a'); 56 | link.href = url; 57 | link.setAttribute('download', 'example.pdf'); 58 | document.body.appendChild(link); 59 | link.click(); 60 | link.parentNode.removeChild(link); 61 | } else { 62 | console.error('Failed to download PDF'); 63 | } 64 | } catch (error) { 65 | console.log("error message => " + error); 66 | } 67 | } */ 68 | 69 | async function handleSubmit() { 70 | console.log(inputData); 71 | setLoading(true); 72 | try { 73 | if (inputData.prompt === "" || inputData.customization === "") { 74 | alert("data is not filled"); 75 | return; 76 | } 77 | const response = await fetch("http://localhost:5000/", { 78 | method: "POST", 79 | headers: { 80 | "Content-Type": "application/json", 81 | }, 82 | body: JSON.stringify(inputData), 83 | }); 84 | if (response.ok) { 85 | console.log(response.statusText); 86 | alert 87 | } else { 88 | console.log(response); 89 | alert(response.statusText); 90 | throw new Error(); 91 | } 92 | } catch (error) { 93 | console.log("error message => " + error); 94 | } 95 | setLoading(false); 96 | 97 | } 98 | 99 | return ( 100 | <> 101 | 102 |
103 |
104 | 105 | What is your Story?{" "} 106 | * 107 | 108 | 114 |
115 |
116 | 117 | Your Comic style? * 118 | 119 | handleChange(e)} 125 | /> 126 |
127 | 128 |
129 | 130 | Key * 131 | 132 | (Get key from here) 133 | {" "} 134 |
135 | handleChange(e)} 142 | ref={keyRef} 143 | /> 144 |
145 |
146 |
147 | {loading ? ( 148 | 151 | ) : ( 152 | 158 | )} 159 |
160 |
161 | 162 | ); 163 | } 164 | -------------------------------------------------------------------------------- /server/utils/Controller.js: -------------------------------------------------------------------------------- 1 | const { GoogleGenerativeAI } = require("@google/generative-ai"); 2 | const fs = require("fs"); 3 | const { createCanvas, loadImage } = require("canvas"); 4 | require("dotenv").config(); 5 | 6 | const genAI = new GoogleGenerativeAI(process.env.geminiAI); 7 | 8 | const engineId = "stable-diffusion-v1-6"; 9 | const apiHost = process.env.API_HOST ?? "https://api.stability.ai"; 10 | const apiKey = process.env.STABLE_DIFFUSION_KEY; 11 | 12 | const writeDialogue = async (person, Imgpath, speech, imageNumber) => { 13 | try { 14 | const image = await loadImage(Imgpath); 15 | const canvas = createCanvas(image.width, image.height); 16 | const ctx = canvas.getContext("2d"); 17 | 18 | ctx.drawImage(image, 0, 0, image.width, image.height); 19 | ctx.fillStyle = "white"; 20 | ctx.strokeRect(10, canvas.height - 50, canvas.width, 200); 21 | ctx.fillRect(10, canvas.height - 50, canvas.width, 190); 22 | 23 | const out = fs.createWriteStream(`./final/main/${imageNumber}.png`); 24 | const stream = canvas.createPNGStream(); 25 | stream.pipe(out); 26 | 27 | out.on("finish", () => { 28 | ctx.font = "italic bold 12px serif"; 29 | ctx.fillStyle = "black"; 30 | ctx.strokeStyle = "red"; 31 | ctx.lineWidth = 3; 32 | ctx.textAlign = "left"; 33 | const textX = 10; 34 | const textY = canvas.height - 30; 35 | ctx.fillText(`${person}: ${speech}`, textX, textY); 36 | 37 | const out = fs.createWriteStream(`./final/main/${imageNumber}.png`); 38 | const stream = canvas.createPNGStream(); 39 | stream.pipe(out); 40 | 41 | out.on("finish", () => { 42 | ctx.shadowColor = "#FFFFFF"; 43 | ctx.shadowBlur = 50; 44 | ctx.lineJoin = "miter"; 45 | ctx.lineCap = "butt"; 46 | ctx.lineWidth = 4; 47 | ctx.strokeStyle = "black"; 48 | ctx.strokeRect(0, 0, image.width, image.height); 49 | 50 | const out = fs.createWriteStream(`./final/main/${imageNumber}.png`); 51 | const stream = canvas.createPNGStream(); 52 | stream.pipe(out); 53 | 54 | out.on("finish", () => { 55 | console.log("Image generated"); 56 | 57 | return; 58 | }); 59 | }); 60 | }); 61 | } catch (error) { 62 | console.error("An error occurred:", error); 63 | } 64 | }; 65 | 66 | const generateImages = async (person, speech, features, diffusionKey) => { 67 | try { 68 | const response = await fetch( 69 | `${apiHost}/v1/generation/${engineId}/text-to-image`, 70 | { 71 | method: "POST", 72 | headers: { 73 | "Content-Type": "application/json", 74 | Accept: "application/json", 75 | Authorization: `Bearer ${apiKey}`, 76 | }, 77 | body: JSON.stringify({ 78 | text_prompts: [ 79 | { 80 | text: `A single classic superhero comic panel. In the foreground stands ${person}, a ${features} style . They have a determined expression and a speech bubble pops out from above their head. Inside the speech bubble, write in bold, dynamic lettering: "${speech}".`, 81 | }, 82 | ], 83 | cfg_scale: 30, 84 | height: 512, 85 | width: 512, 86 | steps: 30, 87 | samples: 1, 88 | seed: 992446758, 89 | style_preset: "comic-book", 90 | }), 91 | } 92 | ); 93 | if (!response.ok) { 94 | throw new Error(`Non-200 response: ${await response.text()}`); 95 | } 96 | 97 | const responseJSON = await response.json(); 98 | if (!fs.existsSync("./out/")) { 99 | fs.mkdirSync("./out/"); 100 | } 101 | responseJSON.artifacts.forEach((image, index) => { 102 | let randomNum = Math.floor(Math.random() * 1000); 103 | let filePath = `./out/v1_txt2img_${randomNum}.png`; 104 | return new Promise((resolve) => { 105 | fs.writeFile(filePath, Buffer.from(image.base64, "base64"), (err) => { 106 | if (!err) { 107 | writeDialogue(person, filePath, speech, randomNum); 108 | } 109 | resolve(); // Resolve the promise after writing the dialogue 110 | }); 111 | }); 112 | }); 113 | } catch (error) { 114 | console.log(error); 115 | return error; 116 | } 117 | }; 118 | 119 | const generateComic = async (jsonOutput, customization, diffusionKey) => { 120 | for (const eachObj of jsonOutput) { 121 | await generateImages(eachObj.speaker, eachObj.dialogue, customization); 122 | } 123 | return "done"; 124 | }; 125 | 126 | const generateMapFromText = (text) => { 127 | let lines = text.split("\n"); 128 | let jsonOutput = []; 129 | 130 | lines.forEach((line) => { 131 | if (line.trim() !== "") { 132 | let parts = line.split(":"); 133 | let speaker = parts[0].trim().replace(/\*\*/g, ""); 134 | let dialogue = parts.slice(1).join(":").trim(); 135 | jsonOutput.push({ speaker: speaker, dialogue: dialogue }); 136 | } 137 | }); 138 | 139 | return JSON.stringify(jsonOutput, null, 2); 140 | }; 141 | 142 | const textToComic = async (userText, customization, diffusionKey) => { 143 | return new Promise(async (resolve, reject) => { 144 | try { 145 | const model = genAI.getGenerativeModel({ model: "gemini-pro" }); 146 | const prompt = `Convert the following boring text into a comic style conversation (make conversation smaller) between characters while retaining information . Try to keep the characters as people from the story. Keep a line break after each dialogue and don't include words like Scene 1, narration context and scenes etc. take the name of the character which is makred in double quote and not character number: \n\n\n ${userText}`; 147 | const result = await model.generateContent(prompt); 148 | const response = result.response.text(); 149 | const jsonOutput = generateMapFromText(response); 150 | await generateComic(JSON.parse(jsonOutput), customization, diffusionKey); 151 | resolve(); 152 | } catch (error) { 153 | reject(error); 154 | } 155 | }); 156 | }; 157 | 158 | module.exports = { 159 | textToComic, 160 | }; 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Comic-cult 2 | 3 | ## redbull gives you wings 💸we give you comic 🖼️ 4 | 5 | ![banner](https://github.com/Girishbari/comic-cult/assets/38005544/8f7228f3-1fa9-488d-9147-2ff3dbbae0bb) 6 | 7 | 8 | 9 | ## ℹ️ Project description 10 | 11 | Transform your stories into captivating comic art with dialogue effortlessly! Our project harnesses the power of GemeiAI and stable diffusion for processing and generating stunning images and dialogues. Utilizing feature flags, you can seamlessly toggle environment variables on and off as needed. The final masterpiece is delivered straight to your inbox as a beautifully crafted comic PDF. Enjoy the magic of storytelling in a whole new visual dimension! 📧💬🖼️ 12 | 13 | 14 | 15 | ## Demo 💻 16 | 17 | Visit https://palettegram.vercel.app to see the live demo! 18 | 19 | ## ⚒️ Features: 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 |
Feature NameFeature Description
Welcome to Social Winter of S4Dialogue GenerationWith advanced dialogue generation capabilities, your characters come to life on the comic pages. Using sophisticated summary, gemeni generates natural and dynamic dialogues that complement the visuals, enhancing the overall storytelling experience.
Welcome to Social Winter of S4Comic Art Generation: Leveraging stability AI and DreamStudio API keys, Comic Art Generation process is infused with stability and creativity. Stability AI ensures consistent and reliable image processing, while DreamStudio API keys unlock a world of artistic possibilities, allowing us to create captivating comic art with ease.
Welcome to HacktoberfestFlagsmith Feature Flags IntegrationI've implemented flagsmith feature flags into our project, providing you with ultimate control over its functionalities. Whether you need to toggle specific features on or off, our feature flags make it effortless to customize the comic generation process according to your preferences and requirements.
Welcome to HacktoberfestEmail Delivery of Comic PDF:Seamlessly integrating nodemailer into our workflow, I've streamlined the process of delivering your comic PDF straight to your inbox. Nodemailer provides a robust and reliable solution for sending emails programmatically, ensuring that your comic masterpiece reaches its destination efficiently and securely.
49 | 50 | ## 🤔 What challenges I ran into 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
Comic Art generationI wanted to make something that is of fun and also gives me a lot of learning I used apis of openAi even utilized the langchain for some instances but wanted to take my level above, I wanted to play around with images that directed me of using any stable diffusion model (which I had no Idea how to use) I tried running model locally also used google collab however all these things made me more perplexed at the end I was in the need of some Stable diffusion API and key which dream-studio and Stability AI provided
Workflow/pipeline of makingProbably I worked for this part mostly such a pipeline was the backbone of this project, I was too confused and wanted to leave project for once however, I slowly worked of making a streamlined flow of generating dialogues (gemini helped) and then getting images(Stability API helped) based on that dialogue, adding dialogues onto images(Canvas helped), all these steps I wanted to be reliable and smooth which could provide happy user experience
Using promisesI have only heard and learned about promises and never utilized them in my any of the project until now, oh man they were life saver, they helped me in making and flow better and workable, I literally now want to use promises everywhere, I really got the understand of promises, async await, .then and catch
failure of express func and integration of nodemailerI am using express for my backend to serve front of various routes I never thought that res.sendfile, res.download would be so pathetic to use, they are made to sendfile from backend to frontend and guess what they were giving errors for the same, almost at this point also I am thinking to use them but they were so un-useful but then nodemailer came, I had this thought in back of my mind that if express functions does not work I will try sending pdf to user mail and using nodemailer this was so useful that I never imagined basically in few minutes I did this
71 | 72 | ## 💻 Tech Stack 73 | 74 | - [Next.js](https://nextjs.org) - Next.js is an open-source web development framework. 75 | - [TypeScript](https://www.typescriptlang.org) - TypeScript is a free and open-source high-level programming language. 76 | - [Tailwind CSS](https://tailwindcss.com) - Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML. 77 | - [Daisy UI](https://tailwindcss.com) - It has inbuilt tailwind component which can be used 78 | - [Express](https://tailwindcss.com) - It is use to make server endpoint and serve front end as needed 79 | - [flagsmith](https://tailwindcss.com) - Manage feature flags across web, mobile, and server side applications 80 | - [Gemini AI](https://tailwindcss.com) - It has api which could be use ful to make prompt and get output 81 | - [Nodemailer](https://tailwindcss.com) - Used to send mail or attachment programmatically 82 | 83 | ## 🔨 Locally install 84 | 85 | To run this project, you will need to add the following environment variables to your .env file (use .env.example for reference) 86 | 87 | [Gemini AI](https://ai.google.dev/docs): `API_KEY` 88 | 89 | [Dream Studio](https://dreamstudio.ai/): `API_KEY` 90 | 91 | [Setup Flagsmit instance and get flagsmith API](https://docs.flagsmith.com/quickstart) : `API_Key` 92 | 93 | [Nodemailer](https://stackoverflow.com/questions/45478293/username-and-password-not-accepted-when-using-nodemailer) user : `your mail` nodemailer pass : `somepass` 94 | 95 | ### Run Locally 96 | 97 | ## Run Locally 98 | 99 | Clone the project 100 | 101 | ```bash 102 | git clone https://github.com/Girishbari/comic-cult 103 | ``` 104 | 105 | Go to the project directory 106 | 107 | ```bash 108 | cd comic-cult 109 | ``` 110 | 111 | Install dependencies 112 | 113 | ```bash 114 | npm install 115 | ``` 116 | 117 | ## fill up the API keys 118 | 119 | Start the Front-end 120 | 121 | ```bash 122 | npm run dev 123 | ``` 124 | 125 | Start the back-end 126 | 127 | ```bash 128 | npm run server 129 | ``` 130 | --------------------------------------------------------------------------------