programmersd commited on
Commit
084efc7
·
verified ·
1 Parent(s): 9c4fab2

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -200
app.py DELETED
@@ -1,200 +0,0 @@
1
- """
2
- PDF → Manim Animation Pipeline
3
- Hugging Face Spaces — Gradio 6.x
4
- """
5
-
6
- import asyncio
7
- import atexit
8
- import queue
9
- import threading
10
- import uuid
11
-
12
- import gradio as gr
13
-
14
- from queue_manager import JobQueue, State
15
- from pipeline import run_pipeline
16
-
17
- # ── Asyncio cleanup (suppresses "Invalid file descriptor" noise on shutdown) ──
18
- def _cleanup_event_loop():
19
- try:
20
- loop = asyncio.get_event_loop()
21
- if not loop.is_closed():
22
- loop.close()
23
- except Exception:
24
- pass
25
-
26
- atexit.register(_cleanup_event_loop)
27
-
28
- # ── Global job queue ──────────────────────────────────────────────────────────
29
- job_queue = JobQueue(max_workers=8, max_jobs=100)
30
-
31
-
32
- # ── Streaming pipeline ────────────────────────────────────────────────────────
33
- def submit_and_stream(pdf_file, api_key: str):
34
- """
35
- Generator — yields tuples:
36
- (status_md, code_str, code_visible, video_path, video_visible,
37
- zip_path, zip_visible)
38
- live-streamed to the Gradio UI.
39
- """
40
-
41
- def _emit(status, code="", code_vis=False, video=None, vid_vis=False, zip_p=None, zip_vis=False):
42
- return (
43
- status,
44
- code,
45
- gr.update(visible=code_vis),
46
- video,
47
- gr.update(visible=vid_vis),
48
- zip_p,
49
- gr.update(visible=zip_vis),
50
- )
51
-
52
- # ── Validate ──────────────────────────────────────────────────────────────
53
- if pdf_file is None:
54
- yield _emit("❌ Please upload a PDF file.")
55
- return
56
- if not api_key or len(api_key) < 10:
57
- yield _emit("❌ Please enter a valid Gemini API key.")
58
- return
59
- if job_queue.is_full():
60
- yield _emit("⚠️ Queue is full (max 100 jobs). Please try again shortly.")
61
- return
62
-
63
- job_id = uuid.uuid4().hex
64
- pdf_path = pdf_file.name
65
- job_queue.register(job_id)
66
-
67
- # Thread-safe update channel
68
- update_q: queue.Queue = queue.Queue()
69
-
70
- def status_cb(state: State, message: str = "", code: str | None = None):
71
- update_q.put((state, message, code))
72
-
73
- result_holder: dict = {}
74
-
75
- def _run():
76
- try:
77
- result = run_pipeline(
78
- job_id=job_id,
79
- pdf_path=pdf_path,
80
- gemini_api_key=api_key,
81
- status_cb=status_cb,
82
- )
83
- result_holder.update(result)
84
- update_q.put((State.DONE, "✅ Render complete!", result.get("code")))
85
- except Exception as exc:
86
- update_q.put((State.FAILED, f"❌ {exc}", None))
87
- finally:
88
- update_q.put(None) # sentinel
89
-
90
- threading.Thread(target=_run, daemon=True).start()
91
-
92
- icons = {
93
- State.QUEUED: "⏳",
94
- State.RUNNING: "⚙️",
95
- State.DONE: "✅",
96
- State.FAILED: "❌",
97
- }
98
-
99
- code_so_far = ""
100
- yield _emit(f"⏳ **Queued** — Starting…\n\n*Job `{job_id}`*")
101
-
102
- while True:
103
- item = update_q.get()
104
- if item is None:
105
- break
106
-
107
- state, message, code = item
108
- if code:
109
- code_so_far = code
110
-
111
- status_text = (
112
- f"{icons.get(state,'❓')} **{state.value.title()}** — {message}"
113
- f"\n\n*Job `{job_id}`*"
114
- )
115
- is_done = state == State.DONE
116
- is_failed = state == State.FAILED
117
-
118
- yield _emit(
119
- status_text,
120
- code = code_so_far,
121
- code_vis = bool(code_so_far),
122
- video = result_holder.get("video_path") if is_done else None,
123
- vid_vis = is_done,
124
- zip_p = result_holder.get("zip_path") if is_done else None,
125
- zip_vis = is_done,
126
- )
127
-
128
-
129
- # ── UI ────────────────────────────────────────────────────────────────────────
130
- with gr.Blocks(title="PDF → Manim Video") as demo:
131
-
132
- gr.Markdown("# 🎬 PDF → Manim Animation Pipeline\nUpload a PDF and get a downloadable Manim animation.")
133
-
134
- saved_api_key = gr.BrowserState("") # persisted in browser localStorage
135
-
136
- with gr.Row():
137
- # ── Left column: inputs ───────────────────────────────────────────────
138
- with gr.Column(scale=1):
139
- pdf_input = gr.File(label="📄 Upload PDF", file_types=[".pdf"])
140
- api_key_input = gr.Textbox(
141
- label="🔑 Gemini API Key",
142
- placeholder="AIza…",
143
- type="password",
144
- info="Saved in your browser — you only need to enter this once.",
145
- )
146
- submit_btn = gr.Button("🚀 Generate Video", variant="primary")
147
-
148
- # ── Right column: outputs ─────────────────────────────────────────────
149
- with gr.Column(scale=1):
150
- status_md = gr.Markdown("*Submit a job to see live status here.*")
151
-
152
- code_box = gr.Code(
153
- label="📝 Generated Manim Code",
154
- language="python",
155
- visible=False,
156
- interactive=False,
157
- )
158
-
159
- video_player = gr.Video(
160
- label="🎬 Rendered Animation",
161
- visible=False,
162
- interactive=False,
163
- )
164
-
165
- zip_download = gr.File(
166
- label="⬇️ Download Artifacts (.py + .mp4)",
167
- visible=False,
168
- interactive=False,
169
- )
170
-
171
- gr.Markdown(
172
- """
173
- ---
174
- **Notes:** Processing typically takes 2–5 minutes depending on animation complexity.
175
- The artifacts ZIP contains the generated `.py` source and the rendered `.mp4`.
176
- Your API key is never stored server-side.
177
-
178
- Have fun! 🎬 If you liked it, feel free to share it with your friends and family.
179
- """
180
- )
181
-
182
- # ── Restore API key from browser on load ──────────────────────────────────
183
- demo.load(fn=lambda k: k, inputs=[saved_api_key], outputs=[api_key_input])
184
- api_key_input.change(fn=lambda v: v, inputs=[api_key_input], outputs=[saved_api_key])
185
-
186
- # ── Streaming submit ───────────────────────────────────────────────────────
187
- submit_btn.click(
188
- fn=submit_and_stream,
189
- inputs=[pdf_input, api_key_input],
190
- outputs=[status_md, code_box, code_box, video_player, video_player, zip_download, zip_download],
191
- )
192
-
193
-
194
- if __name__ == "__main__":
195
- demo.launch(
196
- theme=gr.themes.Soft(),
197
- ssr_mode=False,
198
- server_name="0.0.0.0",
199
- server_port=7860,
200
- )