Imaginethat commited on
Commit
6b88f40
·
verified ·
1 Parent(s): b259b04

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -107
app.py CHANGED
@@ -4,40 +4,23 @@ import os
4
  import pathlib
5
  import shutil
6
  import subprocess
7
- import sys
8
  from typing import List, Tuple
9
 
10
  import gradio as gr
11
 
12
-
13
- def pick_data_root() -> pathlib.Path:
14
- # Prefer /data if it exists & is writable; otherwise fall back to /tmp/aot
15
- candidates = [pathlib.Path("/data"), pathlib.Path("/tmp/aot")]
16
- for p in candidates:
17
- try:
18
- p.mkdir(parents=True, exist_ok=True)
19
- test = p / ".write_test"
20
- test.write_text("ok", encoding="utf-8")
21
- test.unlink()
22
- return p
23
- except Exception:
24
- continue
25
- return pathlib.Path("/tmp/aot")
26
-
27
-
28
- DATA_ROOT = pathlib.Path(os.environ.get("DATA_ROOT", str(pick_data_root())))
29
  INCOMING_DIR = DATA_ROOT / "incoming_videos"
30
  STANDARDIZED_DIR = DATA_ROOT / "standardized_videos"
31
- RUNS_DIR = DATA_ROOT / "dataset" / "runs"
32
 
33
 
34
  def ensure_dirs() -> None:
35
- for d in (INCOMING_DIR, STANDARDIZED_DIR, RUNS_DIR):
36
- d.mkdir(parents=True, exist_ok=True)
37
 
38
 
39
  def save_uploads(files: List[str]) -> List[pathlib.Path]:
40
- saved: List[pathlib.Path] = []
41
  for fp in files:
42
  src = pathlib.Path(fp)
43
  dst = INCOMING_DIR / src.name
@@ -75,41 +58,32 @@ def standardize_video(input_path: pathlib.Path) -> Tuple[pathlib.Path, str]:
75
  str(output_path),
76
  ]
77
  p = subprocess.run(cmd, capture_output=True, text=True)
78
- log = (p.stdout or "") + ("\n" if p.stdout else "") + (p.stderr or "")
79
  if p.returncode != 0:
80
  raise RuntimeError(f"ffmpeg failed for {input_path.name}\n{log}")
81
  return output_path, log
82
 
83
 
84
- def run_avocado(input_path: pathlib.Path, output_path: pathlib.Path) -> Tuple[dict, str]:
85
- """
86
- Runs AVoCaDO analysis via avocado_infer.py.
87
- This is NON-OPTIONAL: if it fails, the whole run fails.
88
- """
89
  cmd = [
90
- sys.executable,
91
  "avocado_infer.py",
92
  "--input",
93
- str(input_path),
94
  "--output",
95
- str(output_path),
96
  ]
97
  p = subprocess.run(cmd, capture_output=True, text=True)
98
- log = (p.stdout or "") + ("\n" if p.stdout else "") + (p.stderr or "")
99
 
100
- output_text = ""
101
- if output_path.exists():
102
- output_text = output_path.read_text(encoding="utf-8")
103
 
104
- result = {
105
- "success": p.returncode == 0,
106
- "returncode": p.returncode,
107
- "stdout": p.stdout,
108
- "stderr": p.stderr,
109
- "output_file": str(output_path),
110
- "output_json": output_text,
111
- }
112
- return result, log
113
 
114
 
115
  def write_jsonl(path: pathlib.Path, rows: List[dict]) -> None:
@@ -127,68 +101,60 @@ def process_videos(files: List[str], include_std_videos: bool) -> Tuple[str, Lis
127
  run_dir = RUNS_DIR / run_label
128
  run_dir.mkdir(parents=True, exist_ok=True)
129
 
130
- logs: List[str] = []
131
- records: List[dict] = []
132
 
 
133
  logs.append(f"Run label: {run_label}")
134
- logs.append(f"DATA_ROOT: {DATA_ROOT}")
135
  logs.append(f"Run dir: {run_dir}")
136
 
137
- saved_paths = save_uploads(files)
138
-
139
  for i, input_path in enumerate(saved_paths, start=1):
140
  logs.append(f"\n=== [{i}/{len(saved_paths)}] {input_path.name} ===")
141
 
142
  standardized_path, ffmpeg_log = standardize_video(input_path)
143
  logs.append("[ffmpeg]\n" + ffmpeg_log.strip())
144
 
145
- avocado_output_path = run_dir / f"{standardized_path.stem}_avocado.json"
146
- avocado_result, avocado_log = run_avocado(standardized_path, avocado_output_path)
147
-
148
- logs.append("[avocado]\n" + avocado_log.strip())
149
-
150
- if not avocado_result["success"]:
151
- # AVoCaDO is the whole point: fail hard.
152
- raise RuntimeError(
153
- f"AVoCaDO failed for {standardized_path.name}\n\n{avocado_result['stderr']}"
154
- )
155
-
156
- record = {
157
- "run_label": run_label,
158
- "source_file": str(input_path),
159
- "standardized_file": str(standardized_path),
160
- "avocado_output_file": str(avocado_output_path),
161
- "avocado": {
162
- "success": avocado_result["success"],
163
- "returncode": avocado_result["returncode"],
164
- "stdout": avocado_result["stdout"],
165
- "stderr": avocado_result["stderr"],
166
- },
167
- "processed_at": datetime.datetime.utcnow().isoformat() + "Z",
168
- }
169
- records.append(record)
170
 
171
  if include_std_videos:
172
  shutil.copy2(standardized_path, run_dir / standardized_path.name)
173
 
 
 
 
 
 
 
 
 
 
 
 
174
  annotations_path = run_dir / "annotations.jsonl"
175
  write_jsonl(annotations_path, records)
176
 
177
  manifest_path = run_dir / "manifest.json"
178
- manifest_payload = {
179
- "run_label": run_label,
180
- "created_at": datetime.datetime.utcnow().isoformat() + "Z",
181
- "count": len(records),
182
- "records": records,
183
- }
184
  manifest_path.write_text(
185
- json.dumps(manifest_payload, ensure_ascii=False, indent=2),
 
 
 
 
 
 
 
 
 
186
  encoding="utf-8",
187
  )
188
 
189
- # Zip the entire run folder (includes per-video *_avocado.json files)
190
- zip_base = str(run_dir) # make_archive wants "path without extension"
191
- zip_path = shutil.make_archive(zip_base, "zip", root_dir=run_dir)
192
 
193
  status = (
194
  "Processing complete.\n"
@@ -197,12 +163,7 @@ def process_videos(files: List[str], include_std_videos: bool) -> Tuple[str, Lis
197
  f"Manifest: {manifest_path}\n"
198
  f"Run zip: {zip_path}"
199
  )
200
-
201
- downloads = [
202
- str(annotations_path),
203
- str(manifest_path),
204
- str(zip_path),
205
- ]
206
  return status, downloads, "\n".join(logs)
207
 
208
 
@@ -210,35 +171,28 @@ def build_app() -> gr.Blocks:
210
  with gr.Blocks() as demo:
211
  gr.Markdown(
212
  "# Avocado On Toast\n"
213
- "Upload videos → standardize with ffmpeg → **run AVoCaDO** → download outputs."
214
  )
215
 
216
- files = gr.File(label="Upload videos", file_count="multiple", type="filepath")
217
-
218
- include_std_videos = gr.Checkbox(
219
- label="Include standardized videos inside the run .zip (bigger downloads)",
220
- value=False,
221
- )
222
 
223
  run_button = gr.Button("Process videos")
224
-
225
  status = gr.Textbox(label="Status", lines=6)
226
  downloads = gr.File(label="Downloads", file_count="multiple")
227
- logs = gr.Textbox(label="Logs (ffmpeg + AVoCaDO)", lines=22)
228
 
229
- run_button.click(
230
- process_videos,
231
- inputs=[files, include_std_videos],
232
- outputs=[status, downloads, logs],
233
- )
234
 
235
  gr.Markdown(
236
- "Note: The Space **Files** tab shows the git repo only. "
237
- "Runtime outputs live under `/tmp/...` or `/data/...`, so you access them via the downloads above."
238
  )
239
 
240
  return demo
241
 
242
 
243
  if __name__ == "__main__":
244
- build_app().launch(show_error=True)
 
 
4
  import pathlib
5
  import shutil
6
  import subprocess
 
7
  from typing import List, Tuple
8
 
9
  import gradio as gr
10
 
11
+ DATA_ROOT = pathlib.Path(os.environ.get("DATA_ROOT", "/tmp/aot"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  INCOMING_DIR = DATA_ROOT / "incoming_videos"
13
  STANDARDIZED_DIR = DATA_ROOT / "standardized_videos"
14
+ RUNS_DIR = DATA_ROOT / "runs"
15
 
16
 
17
  def ensure_dirs() -> None:
18
+ for directory in (INCOMING_DIR, STANDARDIZED_DIR, RUNS_DIR):
19
+ directory.mkdir(parents=True, exist_ok=True)
20
 
21
 
22
  def save_uploads(files: List[str]) -> List[pathlib.Path]:
23
+ saved = []
24
  for fp in files:
25
  src = pathlib.Path(fp)
26
  dst = INCOMING_DIR / src.name
 
58
  str(output_path),
59
  ]
60
  p = subprocess.run(cmd, capture_output=True, text=True)
61
+ log = (p.stdout or "") + "\n" + (p.stderr or "")
62
  if p.returncode != 0:
63
  raise RuntimeError(f"ffmpeg failed for {input_path.name}\n{log}")
64
  return output_path, log
65
 
66
 
67
+ def run_avocado(standardized_video: pathlib.Path, output_json: pathlib.Path) -> Tuple[dict, str]:
 
 
 
 
68
  cmd = [
69
+ "python",
70
  "avocado_infer.py",
71
  "--input",
72
+ str(standardized_video),
73
  "--output",
74
+ str(output_json),
75
  ]
76
  p = subprocess.run(cmd, capture_output=True, text=True)
77
+ log = (p.stdout or "") + "\n" + (p.stderr or "")
78
 
79
+ if p.returncode != 0:
80
+ return {"success": False, "returncode": p.returncode, "stdout": p.stdout, "stderr": p.stderr}, log
 
81
 
82
+ payload = {}
83
+ if output_json.exists():
84
+ payload = json.loads(output_json.read_text(encoding="utf-8"))
85
+
86
+ return {"success": True, "returncode": 0, "output_path": str(output_json), "payload": payload}, log
 
 
 
 
87
 
88
 
89
  def write_jsonl(path: pathlib.Path, rows: List[dict]) -> None:
 
101
  run_dir = RUNS_DIR / run_label
102
  run_dir.mkdir(parents=True, exist_ok=True)
103
 
104
+ logs = []
105
+ records = []
106
 
107
+ saved_paths = save_uploads(files)
108
  logs.append(f"Run label: {run_label}")
 
109
  logs.append(f"Run dir: {run_dir}")
110
 
 
 
111
  for i, input_path in enumerate(saved_paths, start=1):
112
  logs.append(f"\n=== [{i}/{len(saved_paths)}] {input_path.name} ===")
113
 
114
  standardized_path, ffmpeg_log = standardize_video(input_path)
115
  logs.append("[ffmpeg]\n" + ffmpeg_log.strip())
116
 
117
+ avocado_out = run_dir / f"{standardized_path.stem}_avocado.json"
118
+ avo_result, avo_log = run_avocado(standardized_path, avocado_out)
119
+ logs.append("[avocado]\n" + avo_log.strip())
120
+
121
+ # AVoCaDO is the entire point: fail hard if it fails.
122
+ if not avo_result["success"]:
123
+ raise RuntimeError(f"AVoCaDO failed for {standardized_path.name}:\n{avo_result.get('stderr','')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  if include_std_videos:
126
  shutil.copy2(standardized_path, run_dir / standardized_path.name)
127
 
128
+ records.append(
129
+ {
130
+ "run_label": run_label,
131
+ "source_file": str(input_path),
132
+ "standardized_file": str(standardized_path),
133
+ "avocado_output_file": str(avocado_out),
134
+ "avocado": avo_result,
135
+ "processed_at": datetime.datetime.utcnow().isoformat() + "Z",
136
+ }
137
+ )
138
+
139
  annotations_path = run_dir / "annotations.jsonl"
140
  write_jsonl(annotations_path, records)
141
 
142
  manifest_path = run_dir / "manifest.json"
 
 
 
 
 
 
143
  manifest_path.write_text(
144
+ json.dumps(
145
+ {
146
+ "run_label": run_label,
147
+ "created_at": datetime.datetime.utcnow().isoformat() + "Z",
148
+ "count": len(records),
149
+ "records": records,
150
+ },
151
+ ensure_ascii=False,
152
+ indent=2,
153
+ ),
154
  encoding="utf-8",
155
  )
156
 
157
+ zip_path = shutil.make_archive(str(run_dir), "zip", root_dir=run_dir)
 
 
158
 
159
  status = (
160
  "Processing complete.\n"
 
163
  f"Manifest: {manifest_path}\n"
164
  f"Run zip: {zip_path}"
165
  )
166
+ downloads = [str(annotations_path), str(manifest_path), str(zip_path)]
 
 
 
 
 
167
  return status, downloads, "\n".join(logs)
168
 
169
 
 
171
  with gr.Blocks() as demo:
172
  gr.Markdown(
173
  "# Avocado On Toast\n"
174
+ "Upload → standardize → **run AVoCaDO (audio+visual)** → download results."
175
  )
176
 
177
+ with gr.Row():
178
+ files = gr.File(label="Upload videos", file_count="multiple", type="filepath")
179
+ include_std_videos = gr.Checkbox(label="Include standardized videos in zip", value=False)
 
 
 
180
 
181
  run_button = gr.Button("Process videos")
 
182
  status = gr.Textbox(label="Status", lines=6)
183
  downloads = gr.File(label="Downloads", file_count="multiple")
184
+ logs = gr.Textbox(label="Logs", lines=20)
185
 
186
+ run_button.click(process_videos, inputs=[files, include_std_videos], outputs=[status, downloads, logs])
 
 
 
 
187
 
188
  gr.Markdown(
189
+ "The Space **Files** tab is your git repo. Runtime outputs are created under `/tmp/...` "
190
+ "and are surfaced here via the download widgets."
191
  )
192
 
193
  return demo
194
 
195
 
196
  if __name__ == "__main__":
197
+ app = build_app()
198
+ app.launch(show_error=True)