Spaces:
Sleeping
Sleeping
import os | |
import gradio as gr | |
from gradio_client import Client, handle_file | |
import tempfile | |
import logging | |
import shutil | |
import requests | |
# Configure logging | |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
logger = logging.getLogger(__name__) | |
# Get the token from environment variables | |
hf_token = os.environ.get('HF_TOKEN') | |
if not hf_token: | |
logger.warning("HF_TOKEN environment variable not set. Connections to private spaces will fail.") | |
logger.warning("Please set the HF_TOKEN environment variable with your Hugging Face token.") | |
# Backend space name - update this to your private space | |
BACKEND_SPACE = "skallewag/prepperoni-backend" | |
# Initialize client as None first | |
client = None | |
connection_error = None | |
# Try to initialize the client | |
try: | |
client = Client( | |
BACKEND_SPACE, | |
hf_token=hf_token | |
) | |
# Check available endpoints | |
endpoints = client.endpoints | |
logger.info(f"Successfully connected to backend. Available endpoints: {endpoints}") | |
except Exception as e: | |
connection_error = str(e) | |
logger.error(f"Error connecting to backend space '{BACKEND_SPACE}': {str(e)}") | |
logger.error("This might be due to a missing or invalid HF_TOKEN, or the backend space may be offline or private.") | |
# Custom CSS | |
css = """ | |
.error-box { | |
background-color: #ffebe9; | |
border: 1px solid #e9484d; | |
padding: 16px; | |
border-radius: 8px; | |
margin-bottom: 20px; | |
} | |
""" | |
# Create a simple interface that forwards to your private Space | |
with gr.Blocks(title="Prepperoni - Premiere Pro to XML Converter", css=css) as app: | |
# Add a title and description | |
gr.Markdown("# Prepperoni ๐") | |
gr.Markdown("Convert Adobe Premiere Pro project files to XML format for use in other video editing software.") | |
# Display a warning if there's a connection error | |
if connection_error: | |
gr.Markdown(f""" | |
โ ๏ธ **Warning: Cannot connect to backend service** | |
There was an error connecting to the processing backend. The app may not function correctly. | |
Error details: {connection_error} | |
If you're the administrator, please make sure the HF_TOKEN environment variable is set correctly in Space settings. | |
""", elem_classes=["error-box"]) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
# Input components | |
file_input = gr.File(label="Upload Premiere Pro Project File (.prproj)", file_types=[".prproj"]) | |
sequence_input = gr.Textbox(label="Sequence Name (optional)", placeholder="Leave blank to use first sequence") | |
convert_btn = gr.Button("Convert to XML", variant="primary") | |
status = gr.Markdown("Upload a Premiere Pro project file to begin.") | |
with gr.Column(scale=1): | |
# Output components | |
output_file = gr.File(label="Generated XML File") | |
output_message = gr.Markdown("") | |
# Add helpful information | |
with gr.Accordion("About Prepperoni", open=False): | |
gr.Markdown(""" | |
## About Prepperoni | |
Prepperoni is a tool for converting Adobe Premiere Pro project files to XML format, | |
allowing you to export projects from Premiere Pro for use in other video editing software | |
like DaVinci Resolve, Final Cut Pro, and more. | |
### How to use: | |
1. Upload your Premiere Pro project file (.prproj) | |
2. Optionally enter a sequence name (leave blank to use the first sequence) | |
3. Click "Convert to XML" | |
4. Download the generated XML file | |
### Features: | |
- Converts clips, tracks, and transitions | |
- Preserves clip properties like position, scale, rotation | |
- Handles speed effects and time remapping | |
- Supports nested sequences | |
""") | |
# Function to set the initial processing status | |
def set_processing_status(): | |
return None, "Processing... This may take a minute. Please wait.", "Processing..." | |
# Function to process the file through the backend | |
def process_file(file, sequence_name): | |
try: | |
if not client: | |
return None, "Error: Cannot connect to backend service. Please check the connection error at the top of the page.", "Configuration Error" | |
if file is None: | |
return None, "Error: No file provided", "Please upload a Premiere Pro project file." | |
if not hf_token: | |
return None, "Error: HF_TOKEN environment variable is not set. Unable to connect to backend service.", "Configuration Error" | |
# Save the uploaded file to a temporary path | |
temp_dir = tempfile.mkdtemp() | |
temp_file_path = os.path.join(temp_dir, "project.prproj") | |
# Copy the uploaded file to our temp location | |
shutil.copy(file.name, temp_file_path) | |
# Prepare sequence name (None if empty) | |
seq_name = sequence_name.strip() if sequence_name and sequence_name.strip() else None | |
# Try predicting with the backend | |
try: | |
logger.info(f"Sending file to backend for processing with sequence name: {seq_name}") | |
# Use the process_file function from the backend | |
result = client.predict( | |
handle_file(temp_file_path), | |
seq_name, | |
api_name="/process_file" # Use the actual function name from the backend | |
) | |
logger.info(f"Got result from backend: {result}") | |
except Exception as e: | |
logger.error(f"Error connecting to backend: {str(e)}") | |
return None, f"Error connecting to backend service. Please try again later. ({str(e)})", "Error" | |
# Clean up the temporary directory | |
shutil.rmtree(temp_dir, ignore_errors=True) | |
# Handle the result | |
# The backend returns [file_path, message, analytics_text] | |
try: | |
# Extract file path and message from the result | |
if isinstance(result, (list, tuple)) and len(result) >= 2: | |
xml_path = result[0] | |
message = result[1] | |
analytics = result[2] if len(result) >= 3 else "" | |
else: | |
logger.error(f"Unexpected result format: {result}") | |
return None, "Error: Unexpected response format from backend", "Error" | |
logger.info(f"Extracted XML path: {xml_path}, Message: {message}") | |
# Handle the XML file | |
if xml_path: | |
# Download the XML file if it's a URL | |
if isinstance(xml_path, str) and xml_path.startswith("http"): | |
logger.info(f"Downloading XML file from {xml_path}") | |
headers = {"Authorization": f"Bearer {hf_token}"} if hf_token else {} | |
response = requests.get(xml_path, headers=headers) | |
if response.status_code == 200: | |
# Save to a temporary file | |
temp_dir = tempfile.mkdtemp() | |
xml_local_path = os.path.join(temp_dir, "sequence.xml") | |
with open(xml_local_path, "wb") as f: | |
f.write(response.content) | |
logger.info(f"XML file saved to {xml_local_path}") | |
return xml_local_path, f"{message}\n\n{analytics}", "Success!" | |
else: | |
logger.error(f"Failed to download XML: {response.status_code}") | |
return None, f"Error downloading XML: {response.status_code}", "Error" | |
elif isinstance(xml_path, str) and os.path.exists(xml_path): | |
# It's a local file path that exists | |
logger.info(f"XML file is at local path: {xml_path}") | |
return xml_path, f"{message}\n\n{analytics}", "Success!" | |
else: | |
logger.error(f"Invalid XML path: {xml_path}") | |
return None, "Error: Invalid XML file path returned from backend", "Error" | |
else: | |
logger.warning("No XML file generated") | |
return None, "No XML file was generated", "Error" | |
except Exception as e: | |
logger.exception(f"Error processing result: {e}") | |
return None, f"Error processing backend response: {str(e)}", "Error" | |
except Exception as e: | |
logger.exception("Error in process_file") | |
return None, f"Error occurred: {str(e)}", "Error" | |
# Connect the button to first set status, then process | |
convert_btn.click( | |
fn=set_processing_status, | |
outputs=[output_file, output_message, status] | |
).then( | |
fn=process_file, | |
inputs=[file_input, sequence_input], | |
outputs=[output_file, output_message, status] | |
) | |
# Launch the app | |
if __name__ == "__main__": | |
app.launch(show_error=True) |