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)