Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,7 +5,6 @@ import tempfile
|
|
5 |
import logging
|
6 |
import shutil
|
7 |
import requests
|
8 |
-
from huggingface_hub.utils import RepositoryNotFoundError
|
9 |
|
10 |
# Configure logging
|
11 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
@@ -18,7 +17,7 @@ if not hf_token:
|
|
18 |
logger.warning("Please set the HF_TOKEN environment variable with your Hugging Face token.")
|
19 |
|
20 |
# Backend space name - update this to your private space
|
21 |
-
BACKEND_SPACE = "skallewag/prepperoni-backend"
|
22 |
|
23 |
# Initialize client as None first
|
24 |
client = None
|
@@ -38,7 +37,7 @@ except Exception as e:
|
|
38 |
logger.error(f"Error connecting to backend space '{BACKEND_SPACE}': {str(e)}")
|
39 |
logger.error("This might be due to a missing or invalid HF_TOKEN, or the backend space may be offline or private.")
|
40 |
|
41 |
-
# Custom CSS
|
42 |
css = """
|
43 |
.error-box {
|
44 |
background-color: #ffebe9;
|
@@ -67,14 +66,11 @@ with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as a
|
|
67 |
If you're the administrator, please make sure the HF_TOKEN environment variable is set correctly in Space settings.
|
68 |
""", elem_classes=["error-box"])
|
69 |
|
70 |
-
# State variables for sequence data
|
71 |
-
sequence_list = gr.State([])
|
72 |
-
|
73 |
with gr.Row():
|
74 |
with gr.Column(scale=1):
|
75 |
# Input components
|
76 |
file_input = gr.File(label="Upload Premiere Pro Project File (.prproj)", file_types=[".prproj"])
|
77 |
-
|
78 |
convert_btn = gr.Button("Convert to XML", variant="primary")
|
79 |
status = gr.Markdown("Upload a Premiere Pro project file to begin.")
|
80 |
|
@@ -94,7 +90,7 @@ with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as a
|
|
94 |
|
95 |
### How to use:
|
96 |
1. Upload your Premiere Pro project file (.prproj)
|
97 |
-
2.
|
98 |
3. Click "Convert to XML"
|
99 |
4. Download the generated XML file
|
100 |
|
@@ -105,135 +101,6 @@ with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as a
|
|
105 |
- Supports nested sequences
|
106 |
""")
|
107 |
|
108 |
-
# Function to update the sequence dropdown when a file is uploaded
|
109 |
-
def update_sequence_dropdown(file):
|
110 |
-
if not file:
|
111 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], "Please upload a Premiere Pro project file to begin."
|
112 |
-
|
113 |
-
if not client:
|
114 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], "Error: Cannot connect to backend service."
|
115 |
-
|
116 |
-
try:
|
117 |
-
# Save the uploaded file to a temporary path
|
118 |
-
temp_dir = tempfile.mkdtemp()
|
119 |
-
temp_file_path = os.path.join(temp_dir, "project.prproj")
|
120 |
-
|
121 |
-
# Copy the uploaded file to our temp location
|
122 |
-
shutil.copy(file.name, temp_file_path)
|
123 |
-
|
124 |
-
# Try to get sequences from the backend
|
125 |
-
try:
|
126 |
-
# Try with update_dropdown endpoint
|
127 |
-
result = client.predict(
|
128 |
-
handle_file(temp_file_path),
|
129 |
-
api_name="/update_dropdown"
|
130 |
-
)
|
131 |
-
except Exception as e1:
|
132 |
-
logger.warning(f"Failed with api_name='/update_dropdown': {str(e1)}")
|
133 |
-
try:
|
134 |
-
# Try with index 0
|
135 |
-
result = client.predict(
|
136 |
-
handle_file(temp_file_path),
|
137 |
-
fn_index=0
|
138 |
-
)
|
139 |
-
except Exception as e2:
|
140 |
-
logger.error(f"Error getting sequences: {str(e2)}")
|
141 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], f"Error loading sequences: {str(e2)}"
|
142 |
-
|
143 |
-
# Clean up
|
144 |
-
shutil.rmtree(temp_dir, ignore_errors=True)
|
145 |
-
|
146 |
-
# Process the result based on format
|
147 |
-
sequences = []
|
148 |
-
error_msg = ""
|
149 |
-
|
150 |
-
# Log the raw result for debugging
|
151 |
-
logger.info(f"Raw sequence result: {result}")
|
152 |
-
|
153 |
-
try:
|
154 |
-
# Special handling for Gradio component update objects
|
155 |
-
if isinstance(result, dict) and '__type__' in result and result.get('__type__') == 'update':
|
156 |
-
# Extract sequence choices from the Gradio update object
|
157 |
-
if 'choices' in result:
|
158 |
-
choices = result['choices']
|
159 |
-
|
160 |
-
# Handle nested lists or different formats of choices
|
161 |
-
if isinstance(choices, list) and len(choices) > 0:
|
162 |
-
if isinstance(choices[0], list):
|
163 |
-
# Format is likely [['name1', 'value1'], ['name2', 'value2']]
|
164 |
-
# or [['name1', 'name1'], ['name2', 'name2']]
|
165 |
-
sequences = [item[0] for item in choices] # Take the first item (name)
|
166 |
-
else:
|
167 |
-
# Format is likely ['name1', 'name2', 'name3']
|
168 |
-
sequences = choices
|
169 |
-
else:
|
170 |
-
# Standard response handling for non-update objects
|
171 |
-
if isinstance(result, (list, tuple)):
|
172 |
-
if len(result) > 0:
|
173 |
-
if isinstance(result[0], (list, tuple)):
|
174 |
-
# It's a list of sequences
|
175 |
-
sequences = result[0]
|
176 |
-
else:
|
177 |
-
# The result itself is the list of sequences
|
178 |
-
sequences = result
|
179 |
-
|
180 |
-
if len(result) >= 2 and isinstance(result[1], str):
|
181 |
-
error_msg = result[1]
|
182 |
-
elif isinstance(result, dict):
|
183 |
-
if 'choices' in result:
|
184 |
-
choices = result['choices']
|
185 |
-
if isinstance(choices, list) and len(choices) > 0:
|
186 |
-
if isinstance(choices[0], list):
|
187 |
-
# Format is likely [['name1', 'value1'], ['name2', 'value2']]
|
188 |
-
sequences = [item[0] for item in choices]
|
189 |
-
else:
|
190 |
-
sequences = choices
|
191 |
-
elif 'data' in result:
|
192 |
-
if isinstance(result['data'], (list, tuple)) and len(result['data']) >= 1:
|
193 |
-
sequences = result['data'][0]
|
194 |
-
else:
|
195 |
-
sequences = result['data']
|
196 |
-
elif 'sequences' in result:
|
197 |
-
sequences = result['sequences']
|
198 |
-
|
199 |
-
if 'error' in result:
|
200 |
-
error_msg = result['error']
|
201 |
-
|
202 |
-
# Ensure sequences is a list and contains only unique values
|
203 |
-
if not isinstance(sequences, (list, tuple)):
|
204 |
-
# Try to convert to a list if it's something else
|
205 |
-
if hasattr(sequences, 'items'): # It's a dictionary-like object
|
206 |
-
sequences = list(sequences.values())
|
207 |
-
else:
|
208 |
-
# Try to make it a list with a single item
|
209 |
-
sequences = [sequences]
|
210 |
-
|
211 |
-
# Remove duplicates while preserving order
|
212 |
-
unique_sequences = []
|
213 |
-
for seq in sequences:
|
214 |
-
if seq not in unique_sequences:
|
215 |
-
unique_sequences.append(seq)
|
216 |
-
sequences = unique_sequences
|
217 |
-
|
218 |
-
# Convert any non-string items to strings
|
219 |
-
sequences = [str(seq) for seq in sequences]
|
220 |
-
|
221 |
-
logger.info(f"Processed sequences: {sequences}")
|
222 |
-
except Exception as e:
|
223 |
-
logger.exception(f"Error processing sequences result: {e}")
|
224 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], f"Error processing sequences: {str(e)}"
|
225 |
-
|
226 |
-
if not sequences:
|
227 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], "No sequences found in the project."
|
228 |
-
|
229 |
-
# Update the dropdown with sequence names
|
230 |
-
first_value = sequences[0] if sequences and len(sequences) > 0 else None
|
231 |
-
return gr.Dropdown(choices=sequences, value=first_value, visible=True), sequences, f"Found {len(sequences)} sequences. Select one to convert."
|
232 |
-
|
233 |
-
except Exception as e:
|
234 |
-
logger.exception("Error in update_sequence_dropdown")
|
235 |
-
return gr.Dropdown(choices=[], value=None, visible=False), [], f"Error: {str(e)}"
|
236 |
-
|
237 |
# Function to set the initial processing status
|
238 |
def set_processing_status():
|
239 |
return None, "Processing... This may take a minute. Please wait.", "Processing..."
|
@@ -258,44 +125,17 @@ with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as a
|
|
258 |
shutil.copy(file.name, temp_file_path)
|
259 |
|
260 |
# Prepare sequence name (None if empty)
|
261 |
-
seq_name = sequence_name if sequence_name else None
|
262 |
|
263 |
# Try predicting with the backend
|
264 |
try:
|
265 |
-
logger.info("Sending file to backend for processing
|
266 |
-
#
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
seq_name,
|
273 |
-
api_name="/process_file"
|
274 |
-
)
|
275 |
-
except Exception as e1:
|
276 |
-
logger.warning(f"Failed with api_name='/process_file': {str(e1)}")
|
277 |
-
# Try with index 1 which appears to be the process_file endpoint
|
278 |
-
try:
|
279 |
-
result = client.predict(
|
280 |
-
handle_file(temp_file_path),
|
281 |
-
seq_name,
|
282 |
-
fn_index=1
|
283 |
-
)
|
284 |
-
except Exception as e2:
|
285 |
-
logger.warning(f"Failed with fn_index=1: {str(e2)}")
|
286 |
-
# Try with index 0 as fallback
|
287 |
-
result = client.predict(
|
288 |
-
handle_file(temp_file_path),
|
289 |
-
seq_name,
|
290 |
-
fn_index=0
|
291 |
-
)
|
292 |
-
else:
|
293 |
-
# Just try without specifying endpoint
|
294 |
-
result = client.predict(
|
295 |
-
handle_file(temp_file_path),
|
296 |
-
seq_name
|
297 |
-
)
|
298 |
-
|
299 |
logger.info(f"Got result from backend: {result}")
|
300 |
except Exception as e:
|
301 |
logger.error(f"Error connecting to backend: {str(e)}")
|
@@ -305,97 +145,62 @@ with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as a
|
|
305 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
306 |
|
307 |
# Handle the result
|
308 |
-
#
|
309 |
-
xml_path = None
|
310 |
-
message = "Unknown response format"
|
311 |
-
|
312 |
try:
|
313 |
-
|
314 |
-
|
315 |
xml_path = result[0]
|
316 |
-
message = result[1]
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
xml_path = result['file']
|
321 |
-
elif 'data' in result and isinstance(result['data'], (list, tuple)) and len(result['data']) >= 1:
|
322 |
-
xml_path = result['data'][0]
|
323 |
-
|
324 |
-
if 'message' in result:
|
325 |
-
message = result['message']
|
326 |
-
elif 'data' in result and isinstance(result['data'], (list, tuple)) and len(result['data']) >= 2:
|
327 |
-
message = result['data'][1]
|
328 |
-
|
329 |
-
# Log the extracted values
|
330 |
-
logger.info(f"Extracted from result - xml_path: {xml_path}, message: {message}")
|
331 |
-
except Exception as e:
|
332 |
-
logger.exception(f"Error processing result: {e}")
|
333 |
-
return None, f"Error processing result from backend: {str(e)}", "Error"
|
334 |
-
|
335 |
-
if xml_path:
|
336 |
-
# Check if xml_path is a string
|
337 |
-
if not isinstance(xml_path, str):
|
338 |
-
logger.warning(f"XML path is not a string: {type(xml_path)}")
|
339 |
-
# If it's a dict or complex object, it's likely not a valid path
|
340 |
-
if isinstance(xml_path, (dict, list, tuple)):
|
341 |
-
logger.error(f"Invalid XML path, not a string: {xml_path}")
|
342 |
-
return None, "Error: Invalid response format from backend server", "Error"
|
343 |
-
# Try to convert to string if it's another type
|
344 |
-
xml_path = str(xml_path)
|
345 |
|
346 |
-
|
347 |
-
if xml_path.startswith("{") or xml_path.startswith("["):
|
348 |
-
logger.error(f"XML path appears to be JSON or a data structure, not a path: {xml_path}")
|
349 |
-
return None, "Error: Invalid response format from backend server", "Error"
|
350 |
|
351 |
-
#
|
352 |
-
if xml_path
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
else:
|
366 |
-
logger.error(f"
|
367 |
-
return None,
|
368 |
else:
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
# It's a valid local path
|
375 |
-
logger.info(f"XML file is at local path: {xml_path}")
|
376 |
-
return xml_path, message, "Success!"
|
377 |
-
else:
|
378 |
-
logger.warning(f"No XML file generated. Response: {result}")
|
379 |
-
return None, message or "Failed to generate XML", "Error"
|
380 |
|
381 |
except Exception as e:
|
382 |
logger.exception("Error in process_file")
|
383 |
return None, f"Error occurred: {str(e)}", "Error"
|
384 |
|
385 |
-
# Connect file upload to sequence dropdown update
|
386 |
-
file_input.change(
|
387 |
-
fn=update_sequence_dropdown,
|
388 |
-
inputs=[file_input],
|
389 |
-
outputs=[sequence_dropdown, sequence_list, status]
|
390 |
-
)
|
391 |
-
|
392 |
# Connect the button to first set status, then process
|
393 |
convert_btn.click(
|
394 |
fn=set_processing_status,
|
395 |
outputs=[output_file, output_message, status]
|
396 |
).then(
|
397 |
fn=process_file,
|
398 |
-
inputs=[file_input,
|
399 |
outputs=[output_file, output_message, status]
|
400 |
)
|
401 |
|
|
|
5 |
import logging
|
6 |
import shutil
|
7 |
import requests
|
|
|
8 |
|
9 |
# Configure logging
|
10 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
|
17 |
logger.warning("Please set the HF_TOKEN environment variable with your Hugging Face token.")
|
18 |
|
19 |
# Backend space name - update this to your private space
|
20 |
+
BACKEND_SPACE = "skallewag/prepperoni-backend"
|
21 |
|
22 |
# Initialize client as None first
|
23 |
client = None
|
|
|
37 |
logger.error(f"Error connecting to backend space '{BACKEND_SPACE}': {str(e)}")
|
38 |
logger.error("This might be due to a missing or invalid HF_TOKEN, or the backend space may be offline or private.")
|
39 |
|
40 |
+
# Custom CSS
|
41 |
css = """
|
42 |
.error-box {
|
43 |
background-color: #ffebe9;
|
|
|
66 |
If you're the administrator, please make sure the HF_TOKEN environment variable is set correctly in Space settings.
|
67 |
""", elem_classes=["error-box"])
|
68 |
|
|
|
|
|
|
|
69 |
with gr.Row():
|
70 |
with gr.Column(scale=1):
|
71 |
# Input components
|
72 |
file_input = gr.File(label="Upload Premiere Pro Project File (.prproj)", file_types=[".prproj"])
|
73 |
+
sequence_input = gr.Textbox(label="Sequence Name (optional)", placeholder="Leave blank to use first sequence")
|
74 |
convert_btn = gr.Button("Convert to XML", variant="primary")
|
75 |
status = gr.Markdown("Upload a Premiere Pro project file to begin.")
|
76 |
|
|
|
90 |
|
91 |
### How to use:
|
92 |
1. Upload your Premiere Pro project file (.prproj)
|
93 |
+
2. Optionally enter a sequence name (leave blank to use the first sequence)
|
94 |
3. Click "Convert to XML"
|
95 |
4. Download the generated XML file
|
96 |
|
|
|
101 |
- Supports nested sequences
|
102 |
""")
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
# Function to set the initial processing status
|
105 |
def set_processing_status():
|
106 |
return None, "Processing... This may take a minute. Please wait.", "Processing..."
|
|
|
125 |
shutil.copy(file.name, temp_file_path)
|
126 |
|
127 |
# Prepare sequence name (None if empty)
|
128 |
+
seq_name = sequence_name.strip() if sequence_name and sequence_name.strip() else None
|
129 |
|
130 |
# Try predicting with the backend
|
131 |
try:
|
132 |
+
logger.info(f"Sending file to backend for processing with sequence name: {seq_name}")
|
133 |
+
# Use the /process_file endpoint with index 1
|
134 |
+
result = client.predict(
|
135 |
+
handle_file(temp_file_path),
|
136 |
+
seq_name,
|
137 |
+
fn_index=1 # Use fn_index instead of api_name
|
138 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
logger.info(f"Got result from backend: {result}")
|
140 |
except Exception as e:
|
141 |
logger.error(f"Error connecting to backend: {str(e)}")
|
|
|
145 |
shutil.rmtree(temp_dir, ignore_errors=True)
|
146 |
|
147 |
# Handle the result
|
148 |
+
# The backend returns [file_path, message]
|
|
|
|
|
|
|
149 |
try:
|
150 |
+
# Extract file path and message from the result
|
151 |
+
if isinstance(result, (list, tuple)) and len(result) >= 1:
|
152 |
xml_path = result[0]
|
153 |
+
message = result[1] if len(result) >= 2 else "Conversion successful"
|
154 |
+
else:
|
155 |
+
logger.error(f"Unexpected result format: {result}")
|
156 |
+
return None, "Error: Unexpected response format from backend", "Error"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
|
158 |
+
logger.info(f"Extracted XML path: {xml_path}, Message: {message}")
|
|
|
|
|
|
|
159 |
|
160 |
+
# Handle the XML file
|
161 |
+
if xml_path:
|
162 |
+
# Download the XML file if it's a URL
|
163 |
+
if isinstance(xml_path, str) and xml_path.startswith("http"):
|
164 |
+
logger.info(f"Downloading XML file from {xml_path}")
|
165 |
+
headers = {"Authorization": f"Bearer {hf_token}"} if hf_token else {}
|
166 |
+
response = requests.get(xml_path, headers=headers)
|
167 |
+
|
168 |
+
if response.status_code == 200:
|
169 |
+
# Save to a temporary file
|
170 |
+
temp_dir = tempfile.mkdtemp()
|
171 |
+
xml_local_path = os.path.join(temp_dir, "sequence.xml")
|
172 |
+
with open(xml_local_path, "wb") as f:
|
173 |
+
f.write(response.content)
|
174 |
+
logger.info(f"XML file saved to {xml_local_path}")
|
175 |
+
return xml_local_path, message, "Success!"
|
176 |
+
else:
|
177 |
+
logger.error(f"Failed to download XML: {response.status_code}")
|
178 |
+
return None, f"Error downloading XML: {response.status_code}", "Error"
|
179 |
+
elif isinstance(xml_path, str) and os.path.exists(xml_path):
|
180 |
+
# It's a local file path that exists
|
181 |
+
logger.info(f"XML file is at local path: {xml_path}")
|
182 |
+
return xml_path, message, "Success!"
|
183 |
else:
|
184 |
+
logger.error(f"Invalid XML path: {xml_path}")
|
185 |
+
return None, "Error: Invalid XML file path returned from backend", "Error"
|
186 |
else:
|
187 |
+
logger.warning("No XML file generated")
|
188 |
+
return None, "No XML file was generated", "Error"
|
189 |
+
except Exception as e:
|
190 |
+
logger.exception(f"Error processing result: {e}")
|
191 |
+
return None, f"Error processing backend response: {str(e)}", "Error"
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
|
193 |
except Exception as e:
|
194 |
logger.exception("Error in process_file")
|
195 |
return None, f"Error occurred: {str(e)}", "Error"
|
196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
# Connect the button to first set status, then process
|
198 |
convert_btn.click(
|
199 |
fn=set_processing_status,
|
200 |
outputs=[output_file, output_message, status]
|
201 |
).then(
|
202 |
fn=process_file,
|
203 |
+
inputs=[file_input, sequence_input],
|
204 |
outputs=[output_file, output_message, status]
|
205 |
)
|
206 |
|