Phani1008 commited on
Commit
146f1e2
·
verified ·
1 Parent(s): 170d153

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -0
app.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
3
+ import json
4
+ import textwrap
5
+
6
+ # =========================
7
+ # 1. PAGE CONFIG
8
+ # =========================
9
+ st.set_page_config(
10
+ page_title="AI Story to Movie Scene Generator",
11
+ page_icon="🎬",
12
+ layout="wide"
13
+ )
14
+
15
+ st.title("🎬 AI Story → Movie Scene Generator (Phase 1)")
16
+ st.write(
17
+ """
18
+ Paste a short story, and this app will break it into **cinematic scenes** with:
19
+ - Scene title
20
+ - Setting & mood
21
+ - Characters
22
+ - Short summary
23
+ - A detailed **visual prompt** (for future image generation)
24
+ """
25
+ )
26
+
27
+ # =========================
28
+ # 2. LOAD LLM (FLAN-T5) - CACHED
29
+ # =========================
30
+
31
+ @st.cache_resource
32
+ def load_scene_model():
33
+ model_name = "google/flan-t5-base" # good starting point, can upgrade later
34
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
35
+ model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
36
+ return tokenizer, model
37
+
38
+ tokenizer, scene_model = load_scene_model()
39
+
40
+
41
+ def generate_text(prompt: str, max_new_tokens: int = 256) -> str:
42
+ """
43
+ Helper to generate text from Flan-T5 given an instruction-style prompt.
44
+ """
45
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
46
+ output_ids = scene_model.generate(
47
+ **inputs,
48
+ max_new_tokens=max_new_tokens,
49
+ num_beams=4,
50
+ temperature=0.7,
51
+ top_p=0.95,
52
+ early_stopping=True,
53
+ )
54
+ return tokenizer.decode(output_ids[0], skip_special_tokens=True)
55
+
56
+
57
+ # =========================
58
+ # 3. STORY → CHUNKS → SCENES LOGIC
59
+ # =========================
60
+
61
+ def split_story_into_chunks(story_text: str, max_chars_per_chunk: int = 600):
62
+ """
63
+ Split the story into rough chunks based on paragraphs and length
64
+ so each chunk can be turned into a scene by the model.
65
+ """
66
+ paragraphs = [p.strip() for p in story_text.split("\n") if p.strip()]
67
+
68
+ chunks = []
69
+ current = ""
70
+ for p in paragraphs:
71
+ if len(current) + len(p) + 1 <= max_chars_per_chunk:
72
+ current += "\n" + p
73
+ else:
74
+ if current.strip():
75
+ chunks.append(current.strip())
76
+ current = p
77
+ if current.strip():
78
+ chunks.append(current.strip())
79
+
80
+ return chunks
81
+
82
+
83
+ def chunk_to_scene(chunk_text: str, scene_id: int):
84
+ """
85
+ Convert one story chunk into a structured scene JSON using the LLM.
86
+ """
87
+ prompt = f"""
88
+ You are a movie director's assistant.
89
+
90
+ Read the following part of a story and extract a SINGLE movie scene in structured JSON.
91
+
92
+ Story chunk:
93
+ \"\"\"{chunk_text}\"\"\"
94
+
95
+ Return JSON with the following keys:
96
+ - scene_id (integer)
97
+ - title (short scene title)
98
+ - setting (where, when)
99
+ - characters (list of names)
100
+ - mood (emotional tone, e.g. tense, hopeful)
101
+ - summary (2-3 sentences)
102
+ - visual_prompt (a single detailed description to be used for generating a cinematic image, including lighting, style, camera angle)
103
+
104
+ Only output valid JSON, nothing else.
105
+ """
106
+ raw = generate_text(prompt, max_new_tokens=256)
107
+
108
+ # Try to parse JSON
109
+ try:
110
+ data = json.loads(raw)
111
+ except Exception:
112
+ # Fallback: wrap raw text into a basic structure
113
+ data = {
114
+ "scene_id": scene_id,
115
+ "title": f"Scene {scene_id}",
116
+ "setting": "",
117
+ "characters": [],
118
+ "mood": "",
119
+ "summary": raw.strip(),
120
+ "visual_prompt": raw.strip()
121
+ }
122
+
123
+ # Ensure scene_id is set correctly
124
+ data["scene_id"] = scene_id
125
+ return data
126
+
127
+
128
+ def story_to_scenes(story_text: str):
129
+ """
130
+ Full pipeline: story text -> chunks -> list of scene dicts.
131
+ """
132
+ chunks = split_story_into_chunks(story_text, max_chars_per_chunk=600)
133
+ scenes = []
134
+ for i, chunk in enumerate(chunks, start=1):
135
+ scene = chunk_to_scene(chunk, scene_id=i)
136
+ scenes.append(scene)
137
+ return scenes
138
+
139
+
140
+ # =========================
141
+ # 4. STREAMLIT UI
142
+ # =========================
143
+
144
+ st.subheader("📝 Paste Your Story")
145
+
146
+ default_story = """\
147
+ Once upon a time in a neon city, Aarav wandered the alleys alone.
148
+ He had lost track of time after the government AI marked his family as 'non-compliant'.
149
+
150
+ One night, while standing on a rooftop, he noticed a masked stranger watching him.
151
+ The stranger claimed to know the truth about the city’s AI and its hidden rules.
152
+
153
+ Aarav followed reluctantly, unaware that every step was being monitored by invisible drones.
154
+ """
155
+
156
+ story_text = st.text_area(
157
+ "Paste a short story (3–15 paragraphs works best):",
158
+ value=default_story,
159
+ height=260
160
+ )
161
+
162
+ if st.button("🎬 Generate Scenes"):
163
+ if not story_text.strip():
164
+ st.error("Please paste a story first.")
165
+ else:
166
+ with st.spinner("Breaking story into scenes..."):
167
+ scenes = story_to_scenes(story_text)
168
+
169
+ st.success(f"Generated {len(scenes)} scene(s).")
170
+
171
+ st.markdown("---")
172
+ st.subheader("📚 Generated Scenes")
173
+
174
+ for scene in scenes:
175
+ scene_id = scene.get("scene_id", "?")
176
+ title = scene.get("title", f"Scene {scene_id}")
177
+ with st.expander(f"Scene {scene_id}: {title}", expanded=True):
178
+ st.markdown(f"**Setting:** {scene.get('setting', '')}")
179
+ st.markdown(f"**Mood:** {scene.get('mood', '')}")
180
+ st.markdown(f"**Characters:** {', '.join(scene.get('characters', [])) or 'N/A'}")
181
+
182
+ st.markdown("**Summary:**")
183
+ st.write(scene.get("summary", ""))
184
+
185
+ st.markdown("**Visual Prompt (for future image generation):**")
186
+ st.code(textwrap.fill(scene.get("visual_prompt", ""), width=90), language="text")
187
+
188
+ st.info("✅ Phase 1 complete: story → structured scenes.\n\nNext phases will turn these visual prompts into images and then a video.")
189
+ else:
190
+ st.info("Paste a story and click **Generate Scenes** to begin.")