aminskjen commited on
Commit
3371d60
·
verified ·
1 Parent(s): f2ef360

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +126 -378
app.py CHANGED
@@ -3,451 +3,199 @@ import logging
3
  from flask import Flask, render_template, request, jsonify, session, redirect, url_for, flash
4
  from flask_sqlalchemy import SQLAlchemy
5
  from flask_login import LoginManager, login_user, logout_user, login_required, current_user
 
6
  import cohere
7
  import requests
8
  import uuid
9
  from datetime import datetime
10
- from sqlalchemy.orm import DeclarativeBase
11
 
12
- # Set up logging for debugging
13
- logging.basicConfig(level=logging.DEBUG)
14
 
15
  class Base(DeclarativeBase):
16
  pass
17
 
18
- # Initialize Flask app
19
  app = Flask(__name__)
20
- app.secret_key = os.environ.get("SESSION_SECRET", "default_secret_key_for_dev")
21
-
22
- # Database configuration
23
- database_url = os.environ.get("DATABASE_URL")
24
- if not database_url:
25
- # Fallback for development
26
- database_url = "sqlite:///inkboard.db"
27
 
28
- app.config["SQLALCHEMY_DATABASE_URI"] = database_url
 
29
  app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
30
  app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
31
  'pool_pre_ping': True,
32
  "pool_recycle": 300,
33
  }
34
 
35
- # Initialize database
36
  db = SQLAlchemy(app, model_class=Base)
37
 
38
- # Initialize Flask-Login
39
  login_manager = LoginManager()
40
  login_manager.init_app(app)
41
  login_manager.login_view = 'login'
42
  login_manager.login_message = 'Please log in to access InkBoard'
43
 
44
- # Initialize Cohere client for text generation
45
- COHERE_API_KEY = os.environ.get("COHERE_API_KEY")
46
- if not COHERE_API_KEY:
47
- logging.error("COHERE_API_KEY environment variable not set")
48
- cohere_client = None
49
- else:
50
- cohere_client = cohere.Client(COHERE_API_KEY)
51
-
52
- # Initialize Hugging Face for image generation
53
  HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
54
- HF_IMAGE_MODEL = "stabilityai/stable-diffusion-2-1" # Popular image generation model
 
 
55
 
56
  @login_manager.user_loader
57
  def load_user(user_id):
58
- """Load user for Flask-Login"""
59
  return User.query.get(int(user_id))
60
 
61
  @app.route('/')
62
  def index():
63
- """Main page - redirect to login if not authenticated"""
64
- if current_user.is_authenticated:
65
- return render_template('dashboard.html', user=current_user)
66
- return render_template('index.html')
67
 
68
  @app.route('/register', methods=['GET', 'POST'])
69
  def register():
70
- """User registration"""
71
  if request.method == 'POST':
72
- try:
73
- data = request.get_json() if request.is_json else request.form
74
- username = data.get('username', '').strip()
75
- email = data.get('email', '').strip()
76
- password = data.get('password', '').strip()
77
-
78
- if not username or not email or not password:
79
- return jsonify({'error': 'All fields are required'}), 400
80
-
81
- # Check if user already exists
82
- if User.query.filter_by(username=username).first():
83
- return jsonify({'error': 'Username already exists'}), 400
84
-
85
- if User.query.filter_by(email=email).first():
86
- return jsonify({'error': 'Email already exists'}), 400
87
-
88
- # Create new user
89
- user = User(username=username, email=email)
90
- user.set_password(password)
91
-
92
- db.session.add(user)
93
- db.session.commit()
94
-
95
- # Log in the user
96
- login_user(user)
97
-
98
- if request.is_json:
99
- return jsonify({'success': True, 'redirect': url_for('index')})
100
- else:
101
- flash('Registration successful! Welcome to InkBoard!', 'success')
102
- return redirect(url_for('index'))
103
-
104
- except Exception as e:
105
- logging.error(f"Registration error: {str(e)}")
106
- if request.is_json:
107
- return jsonify({'error': 'Registration failed'}), 500
108
- else:
109
- flash('Registration failed. Please try again.', 'error')
110
- return render_template('register.html')
111
-
112
  return render_template('register.html')
113
 
114
  @app.route('/login', methods=['GET', 'POST'])
115
  def login():
116
- """User login"""
117
  if request.method == 'POST':
118
- try:
119
- data = request.get_json() if request.is_json else request.form
120
- username = data.get('username', '').strip()
121
- password = data.get('password', '').strip()
122
-
123
- if not username or not password:
124
- return jsonify({'error': 'Username and password are required'}), 400
125
-
126
- # Find user by username or email
127
- user = User.query.filter(
128
- (User.username == username) | (User.email == username)
129
- ).first()
130
-
131
- if user and user.check_password(password):
132
- login_user(user)
133
- if request.is_json:
134
- return jsonify({'success': True, 'redirect': url_for('index')})
135
- else:
136
- flash('Welcome back to InkBoard!', 'success')
137
- return redirect(url_for('index'))
138
- else:
139
- if request.is_json:
140
- return jsonify({'error': 'Invalid username or password'}), 401
141
- else:
142
- flash('Invalid username or password', 'error')
143
- return render_template('login.html')
144
-
145
- except Exception as e:
146
- logging.error(f"Login error: {str(e)}")
147
- if request.is_json:
148
- return jsonify({'error': 'Login failed'}), 500
149
- else:
150
- flash('Login failed. Please try again.', 'error')
151
- return render_template('login.html')
152
-
153
  return render_template('login.html')
154
 
155
  @app.route('/logout')
156
  @login_required
157
  def logout():
158
- """User logout"""
159
  logout_user()
160
- flash('You have been logged out', 'info')
161
  return redirect(url_for('index'))
162
 
163
  @app.route('/generate', methods=['POST'])
164
  @login_required
165
  def generate_content():
166
- """Generate story and image from scene description using Cohere and Hugging Face"""
167
- try:
168
- data = request.get_json()
169
- scene_idea = data.get('scene_idea', '').strip()
170
-
171
- if not scene_idea:
172
- return jsonify({'error': 'Please provide a scene idea'}), 400
173
-
174
- if not cohere_client:
175
- return jsonify({'error': 'Cohere API key not configured'}), 500
176
-
177
- # Generate expanded story using Cohere
178
- story_prompt = f"""Transform this scene idea into a vivid, descriptive paragraph that paints a beautiful picture with words. Keep it between 80-150 words, rich in sensory details and atmosphere:
179
-
180
- Scene idea: {scene_idea}
181
-
182
- Write a single, flowing paragraph that brings this scene to life with beautiful imagery and emotions."""
183
-
184
- logging.debug(f"Generating story for scene: {scene_idea}")
185
-
186
- # Use Cohere's generate endpoint for text generation
187
- story_response = cohere_client.generate(
188
- model='command', # Cohere's flagship model
189
- prompt=story_prompt,
190
- max_tokens=200, # Limit to keep response concise (80-150 words)
191
- temperature=0.7,
192
- k=0,
193
- stop_sequences=[],
194
- return_likelihoods='NONE'
195
- )
196
-
197
- expanded_story = story_response.generations[0].text.strip()
198
- logging.debug(f"Generated story: {expanded_story[:100]}...")
199
-
200
- # Generate image using Hugging Face API
201
- image_url = None
202
- if HUGGINGFACE_API_KEY:
203
- try:
204
- image_url = generate_image_hf(scene_idea, expanded_story)
205
- logging.debug(f"Generated image URL: {image_url}")
206
- except Exception as img_error:
207
- logging.warning(f"Image generation failed: {str(img_error)}")
208
- # Continue without image - story generation is primary feature
209
-
210
- # Save to database instead of session
211
- creation_id = str(uuid.uuid4())
212
- creation = Creation(
213
- id=creation_id,
214
- user_id=current_user.id,
215
- scene_idea=scene_idea,
216
- story=expanded_story,
217
- image_url=image_url
218
- )
219
-
220
- db.session.add(creation)
221
- db.session.commit()
222
-
223
- return jsonify({
224
- 'success': True,
225
- 'story': expanded_story,
226
- 'image_url': image_url,
227
- 'creation_id': creation_id
228
- })
229
-
230
- except Exception as e:
231
- logging.error(f"Error generating content: {str(e)}")
232
- return jsonify({'error': f'An error occurred: {str(e)}'}), 500
233
 
234
- def generate_image_hf(scene_idea, story):
235
- """Generate image using Hugging Face API or create a beautiful SVG placeholder"""
236
- try:
237
- # First try Hugging Face API
238
- if HUGGINGFACE_API_KEY:
239
- image_prompt = f"A beautiful artistic illustration of: {scene_idea}. Style: dreamy, soft colors, high quality digital art, atmospheric"
240
-
241
- # Try multiple models in case one fails
242
- models_to_try = [
243
- "runwayml/stable-diffusion-v1-5",
244
- "stabilityai/stable-diffusion-2-1",
245
- "CompVis/stable-diffusion-v1-4"
246
- ]
247
-
248
- for model in models_to_try:
249
- try:
250
- api_url = f"https://api-inference.huggingface.co/models/{model}"
251
- headers = {"Authorization": f"Bearer {HUGGINGFACE_API_KEY}"}
252
-
253
- # Send request to Hugging Face
254
- response = requests.post(
255
- api_url,
256
- headers=headers,
257
- json={"inputs": image_prompt},
258
- timeout=60
259
- )
260
-
261
- if response.status_code == 200:
262
- # Save the image temporarily and return a placeholder URL
263
- # In a real app, you'd upload to cloud storage
264
- import base64
265
- image_data = response.content
266
- image_base64 = base64.b64encode(image_data).decode('utf-8')
267
- logging.debug(f"Successfully generated image with model: {model}")
268
- return f"data:image/png;base64,{image_base64}"
269
- else:
270
- logging.warning(f"Model {model} failed: {response.status_code} - {response.text}")
271
- continue
272
-
273
- except Exception as model_error:
274
- logging.warning(f"Model {model} error: {str(model_error)}")
275
- continue
276
-
277
- logging.warning("All Hugging Face models failed, falling back to SVG placeholder")
278
-
279
- # Create a beautiful SVG placeholder based on scene description
280
- return generate_svg_placeholder(scene_idea, story)
281
-
282
- except Exception as e:
283
- logging.error(f"Error generating image: {str(e)}")
284
- return generate_svg_placeholder(scene_idea, story)
285
 
286
- def generate_svg_placeholder(scene_idea, story):
287
- """Generate a beautiful SVG placeholder image based on the scene"""
 
 
 
 
 
 
 
 
 
 
 
 
288
  try:
289
- # Create a color palette based on keywords in the scene
290
- colors = get_scene_colors(scene_idea.lower())
291
-
292
- # Create SVG with gradient background and artistic elements
293
- svg_content = f"""
294
- <svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
295
- <defs>
296
- <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
297
- <stop offset="0%" style="stop-color:{colors['primary']};stop-opacity:1" />
298
- <stop offset="100%" style="stop-color:{colors['secondary']};stop-opacity:1" />
299
- </linearGradient>
300
- <radialGradient id="glow" cx="50%" cy="50%" r="50%">
301
- <stop offset="0%" style="stop-color:white;stop-opacity:0.3" />
302
- <stop offset="100%" style="stop-color:white;stop-opacity:0" />
303
- </radialGradient>
304
- </defs>
305
-
306
- <!-- Background -->
307
- <rect width="400" height="400" fill="url(#bg)" />
308
-
309
- <!-- Artistic elements based on scene -->
310
- {get_scene_elements(scene_idea.lower(), colors)}
311
-
312
- <!-- Glow effect -->
313
- <rect width="400" height="400" fill="url(#glow)" />
314
-
315
- <!-- Scene text -->
316
- <text x="200" y="350" font-family="Arial, sans-serif" font-size="14" fill="white" text-anchor="middle" opacity="0.8">
317
- {scene_idea[:40]}{"..." if len(scene_idea) > 40 else ""}
318
- </text>
319
- </svg>
320
- """
321
-
322
- # Convert SVG to base64 data URL
323
- import base64
324
- svg_base64 = base64.b64encode(svg_content.encode('utf-8')).decode('utf-8')
325
- return f"data:image/svg+xml;base64,{svg_base64}"
326
-
327
  except Exception as e:
328
- logging.error(f"Error generating SVG placeholder: {str(e)}")
329
- return None
330
-
331
- def get_scene_colors(scene_text):
332
- """Get color palette based on scene description"""
333
- # Default colors
334
- colors = {
335
- 'primary': '#4fd1c7',
336
- 'secondary': '#88d8f7',
337
- 'accent': '#a8e6cf'
338
- }
339
-
340
- # Adjust colors based on keywords
341
- if any(word in scene_text for word in ['sunset', 'dawn', 'orange', 'red']):
342
- colors = {'primary': '#ff6b6b', 'secondary': '#ffa726', 'accent': '#ffcc80'}
343
- elif any(word in scene_text for word in ['night', 'dark', 'moon', 'stars']):
344
- colors = {'primary': '#3f51b5', 'secondary': '#1a237e', 'accent': '#7986cb'}
345
- elif any(word in scene_text for word in ['forest', 'green', 'nature', 'tree']):
346
- colors = {'primary': '#4caf50', 'secondary': '#2e7d32', 'accent': '#a5d6a7'}
347
- elif any(word in scene_text for word in ['ocean', 'sea', 'water', 'blue']):
348
- colors = {'primary': '#2196f3', 'secondary': '#0d47a1', 'accent': '#90caf9'}
349
- elif any(word in scene_text for word in ['fire', 'flame', 'hot', 'warm']):
350
- colors = {'primary': '#f44336', 'secondary': '#d32f2f', 'accent': '#ffab91'}
351
-
352
- return colors
353
-
354
- def get_scene_elements(scene_text, colors):
355
- """Generate SVG elements based on scene description"""
356
- elements = []
357
-
358
- # Add different shapes and elements based on keywords
359
- if any(word in scene_text for word in ['mountain', 'cliff', 'hill']):
360
- elements.append(f'<polygon points="0,400 150,200 300,250 400,400" fill="{colors["accent"]}" opacity="0.7" />')
361
- elements.append(f'<polygon points="100,400 250,150 400,200 400,400" fill="{colors["primary"]}" opacity="0.6" />')
362
-
363
- if any(word in scene_text for word in ['sun', 'sunset', 'sunrise']):
364
- elements.append(f'<circle cx="300" cy="100" r="40" fill="#ffeb3b" opacity="0.8" />')
365
- elements.append(f'<circle cx="300" cy="100" r="60" fill="#fff59d" opacity="0.3" />')
366
-
367
- if any(word in scene_text for word in ['moon', 'night']):
368
- elements.append(f'<circle cx="320" cy="80" r="30" fill="#f5f5f5" opacity="0.9" />')
369
- elements.append(f'<circle cx="100" cy="150" r="2" fill="white" opacity="0.8" />')
370
- elements.append(f'<circle cx="150" cy="120" r="1.5" fill="white" opacity="0.7" />')
371
- elements.append(f'<circle cx="200" cy="100" r="1" fill="white" opacity="0.6" />')
372
-
373
- if any(word in scene_text for word in ['tree', 'forest']):
374
- elements.append(f'<ellipse cx="80" cy="300" rx="15" ry="60" fill="{colors["accent"]}" opacity="0.8" />')
375
- elements.append(f'<ellipse cx="120" cy="280" rx="20" ry="70" fill="{colors["primary"]}" opacity="0.7" />')
376
-
377
- if any(word in scene_text for word in ['water', 'ocean', 'lake']):
378
- elements.append(f'<ellipse cx="200" cy="350" rx="150" ry="30" fill="{colors["secondary"]}" opacity="0.6" />')
379
- elements.append(f'<ellipse cx="200" cy="360" rx="180" ry="25" fill="{colors["primary"]}" opacity="0.4" />')
380
-
381
- # Add some abstract artistic elements
382
- elements.append(f'<circle cx="50" cy="80" r="8" fill="white" opacity="0.3" />')
383
- elements.append(f'<circle cx="350" cy="300" r="12" fill="white" opacity="0.2" />')
384
- elements.append(f'<circle cx="300" cy="250" r="6" fill="white" opacity="0.4" />')
385
-
386
- return '\n'.join(elements)
387
 
388
  @app.route('/save_journal', methods=['POST'])
389
  @login_required
390
  def save_journal():
391
- """Save journal entry for a creation"""
392
- try:
393
- data = request.get_json()
394
- creation_id = data.get('creation_id')
395
- journal_entry = data.get('journal_entry', '').strip()
396
-
397
- if not creation_id:
398
- return jsonify({'error': 'Creation ID required'}), 400
399
-
400
- # Find the creation in database
401
- creation = Creation.query.filter_by(
402
- id=creation_id,
403
- user_id=current_user.id
404
- ).first()
405
-
406
- if not creation:
407
- return jsonify({'error': 'Creation not found'}), 404
408
-
409
- creation.journal_entry = journal_entry
410
- creation.updated_at = datetime.utcnow()
411
-
412
- db.session.commit()
413
-
414
- return jsonify({'success': True})
415
-
416
- except Exception as e:
417
- logging.error(f"Error saving journal: {str(e)}")
418
- return jsonify({'error': 'Failed to save journal entry'}), 500
419
 
420
  @app.route('/get_creations')
421
  @login_required
422
  def get_creations():
423
- """Get all user creations"""
424
- try:
425
- creations = Creation.query.filter_by(user_id=current_user.id).order_by(Creation.created_at.desc()).all()
426
-
427
- creations_data = []
428
- for creation in creations:
429
- creations_data.append({
430
- 'id': creation.id,
431
- 'scene_idea': creation.scene_idea,
432
- 'story': creation.story,
433
- 'image_url': creation.image_url,
434
- 'journal_entry': creation.journal_entry,
435
- 'created_at': creation.created_at.isoformat()
436
- })
437
-
438
- return jsonify({'creations': creations_data})
439
-
440
- except Exception as e:
441
- logging.error(f"Error getting creations: {str(e)}")
442
- return jsonify({'error': 'Failed to get creations'}), 500
443
 
444
- # Import models after app and db are defined
445
- from models import User, Creation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
 
447
- # Create database tables
 
 
 
 
448
  with app.app_context():
449
  db.create_all()
450
- logging.info("Database tables created")
451
 
452
  if __name__ == '__main__':
453
- app.run(host='0.0.0.0', port=5000, debug=True)
 
3
  from flask import Flask, render_template, request, jsonify, session, redirect, url_for, flash
4
  from flask_sqlalchemy import SQLAlchemy
5
  from flask_login import LoginManager, login_user, logout_user, login_required, current_user
6
+ from sqlalchemy.orm import DeclarativeBase
7
  import cohere
8
  import requests
9
  import uuid
10
  from datetime import datetime
 
11
 
12
+ # Setup logging
13
+ logging.basicConfig(level=logging.INFO)
14
 
15
  class Base(DeclarativeBase):
16
  pass
17
 
 
18
  app = Flask(__name__)
19
+ app.secret_key = os.environ.get("SESSION_SECRET", os.urandom(24).hex())
 
 
 
 
 
 
20
 
21
+ # Database config
22
+ app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite:///inkboard.db")
23
  app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
24
  app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
25
  'pool_pre_ping': True,
26
  "pool_recycle": 300,
27
  }
28
 
 
29
  db = SQLAlchemy(app, model_class=Base)
30
 
31
+ # Flask-Login setup
32
  login_manager = LoginManager()
33
  login_manager.init_app(app)
34
  login_manager.login_view = 'login'
35
  login_manager.login_message = 'Please log in to access InkBoard'
36
 
37
+ # API keys
38
+ API_KEY = os.environ.get("Api_key")
 
 
 
 
 
 
 
39
  HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
40
+ HF_IMAGE_MODEL = "stabilityai/stable-diffusion-2-1"
41
+
42
+ cohere_client = cohere.Client(API_KEY) if API_KEY else None
43
 
44
  @login_manager.user_loader
45
  def load_user(user_id):
46
+ from models import User
47
  return User.query.get(int(user_id))
48
 
49
  @app.route('/')
50
  def index():
51
+ return render_template('dashboard.html', user=current_user) if current_user.is_authenticated else render_template('index.html')
 
 
 
52
 
53
  @app.route('/register', methods=['GET', 'POST'])
54
  def register():
55
+ from models import User
56
  if request.method == 'POST':
57
+ data = request.get_json() if request.is_json else request.form
58
+ username, email, password = data.get('username', '').strip(), data.get('email', '').strip(), data.get('password', '').strip()
59
+
60
+ if not all([username, email, password]):
61
+ return jsonify({'error': 'All fields are required'}), 400
62
+
63
+ if User.query.filter_by(username=username).first() or User.query.filter_by(email=email).first():
64
+ return jsonify({'error': 'Username or email already exists'}), 400
65
+
66
+ user = User(username=username, email=email)
67
+ user.set_password(password)
68
+
69
+ db.session.add(user)
70
+ db.session.commit()
71
+ login_user(user)
72
+
73
+ return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
74
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  return render_template('register.html')
76
 
77
  @app.route('/login', methods=['GET', 'POST'])
78
  def login():
79
+ from models import User
80
  if request.method == 'POST':
81
+ data = request.get_json() if request.is_json else request.form
82
+ username, password = data.get('username', '').strip(), data.get('password', '').strip()
83
+
84
+ user = User.query.filter((User.username == username) | (User.email == username)).first()
85
+
86
+ if user and user.check_password(password):
87
+ login_user(user)
88
+ return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
89
+
90
+ return jsonify({'error': 'Invalid credentials'}), 401
91
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  return render_template('login.html')
93
 
94
  @app.route('/logout')
95
  @login_required
96
  def logout():
 
97
  logout_user()
 
98
  return redirect(url_for('index'))
99
 
100
  @app.route('/generate', methods=['POST'])
101
  @login_required
102
  def generate_content():
103
+ from models import Creation
104
+ data = request.get_json()
105
+ scene_idea = data.get('scene_idea', '').strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
+ if not scene_idea:
108
+ return jsonify({'error': 'Please provide a scene idea'}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
+ story_prompt = f"""Transform this scene idea into a vivid paragraph:
111
+ Scene idea: {scene_idea}"""
112
+
113
+ story_response = cohere_client.generate(
114
+ model='command',
115
+ prompt=story_prompt,
116
+ max_tokens=200,
117
+ temperature=0.7,
118
+ k=0
119
+ )
120
+
121
+ expanded_story = story_response.generations[0].text.strip()
122
+
123
+ image_url = None
124
  try:
125
+ image_url = generate_image_hf(scene_idea, expanded_story)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  except Exception as e:
127
+ logging.warning(f"Image generation failed: {e}")
128
+
129
+ creation_id = str(uuid.uuid4())
130
+ creation = Creation(
131
+ id=creation_id,
132
+ user_id=current_user.id,
133
+ scene_idea=scene_idea,
134
+ story=expanded_story,
135
+ image_url=image_url
136
+ )
137
+
138
+ db.session.add(creation)
139
+ db.session.commit()
140
+
141
+ return jsonify({'success': True, 'story': expanded_story, 'image_url': image_url, 'creation_id': creation_id})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  @app.route('/save_journal', methods=['POST'])
144
  @login_required
145
  def save_journal():
146
+ from models import Creation
147
+ data = request.get_json()
148
+ creation_id, journal_entry = data.get('creation_id'), data.get('journal_entry', '').strip()
149
+ creation = Creation.query.filter_by(id=creation_id, user_id=current_user.id).first()
150
+ if not creation:
151
+ return jsonify({'error': 'Not found'}), 404
152
+ creation.journal_entry = journal_entry
153
+ creation.updated_at = datetime.utcnow()
154
+ db.session.commit()
155
+ return jsonify({'success': True})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  @app.route('/get_creations')
158
  @login_required
159
  def get_creations():
160
+ from models import Creation
161
+ creations = Creation.query.filter_by(user_id=current_user.id).order_by(Creation.created_at.desc()).all()
162
+ return jsonify({'creations': [{
163
+ 'id': c.id,
164
+ 'scene_idea': c.scene_idea,
165
+ 'story': c.story,
166
+ 'image_url': c.image_url,
167
+ 'journal_entry': c.journal_entry,
168
+ 'created_at': c.created_at.isoformat()
169
+ } for c in creations]})
 
 
 
 
 
 
 
 
 
 
170
 
171
+ def generate_image_hf(scene_idea, story):
172
+ prompt = f"An artistic illustration of: {scene_idea}, dreamy and vivid"
173
+ for model in [
174
+ "runwayml/stable-diffusion-v1-5",
175
+ "stabilityai/stable-diffusion-2-1",
176
+ "CompVis/stable-diffusion-v1-4"
177
+ ]:
178
+ try:
179
+ res = requests.post(
180
+ f"https://api-inference.huggingface.co/models/{model}",
181
+ headers={"Authorization": f"Bearer {HUGGINGFACE_API_KEY}"},
182
+ json={"inputs": prompt},
183
+ timeout=60
184
+ )
185
+ if res.status_code == 200:
186
+ import base64
187
+ return f"data:image/png;base64,{base64.b64encode(res.content).decode()}"
188
+ except Exception as e:
189
+ continue
190
+ return generate_svg_placeholder(scene_idea, story)
191
 
192
+ def generate_svg_placeholder(scene_idea, story):
193
+ return "<svg>Your beautiful fallback SVG here</svg>"
194
+
195
+ # DB setup
196
+ from models import User, Creation
197
  with app.app_context():
198
  db.create_all()
 
199
 
200
  if __name__ == '__main__':
201
+ app.run(host='0.0.0.0', port=5000, debug=True)