import os import tempfile import gradio as gr import eyed3 from PIL import Image import io import shutil # Function to load MP3 file and extract metadata def load_mp3(file_obj): # Create a temporary directory temp_dir = tempfile.mkdtemp() temp_path = os.path.join(temp_dir, "temp.mp3") try: # In Gradio, file_obj is a file path string file_path = file_obj.name # Get the actual file path from Gradio's file component # Copy the file to our temporary location shutil.copy2(file_path, temp_path) # Load the MP3 file with eyed3 audiofile = eyed3.load(temp_path) # Extract metadata if audiofile is None or audiofile.tag is None: # Create a new tag if one doesn't exist if audiofile is None: return None, "Invalid MP3 file", None, None, None, None, None, None, None, None audiofile.initTag() title = audiofile.tag.title or "" artist = audiofile.tag.artist or "" album = audiofile.tag.album or "" year = str(audiofile.tag.getBestDate() or "") track_num = str(audiofile.tag.track_num[0]) if audiofile.tag.track_num and audiofile.tag.track_num[0] else "" genre = str(audiofile.tag.genre or "") comments = audiofile.tag.comments[0].text if audiofile.tag.comments and len(audiofile.tag.comments) > 0 else "" # Extract album art if available cover_art = None for img in audiofile.tag.images: if img.picture_type == 3: # Front cover image_data = img.image_data cover_art = Image.open(io.BytesIO(image_data)) break # Return the file path and metadata return ( temp_path, os.path.basename(file_path), title, artist, album, year, track_num, genre, comments, cover_art ) except Exception as e: print(f"Error loading MP3: {str(e)}") return None, f"Error: {str(e)}", None, None, None, None, None, None, None, None # Function to save changes and provide download def save_changes( file_path, title, artist, album, year, track_num, genre, comments, cover_art, remove_cover_art_option ): if not file_path: return None, "No MP3 file loaded" try: # Load the MP3 file audiofile = eyed3.load(file_path) if audiofile is None: return None, "Failed to load MP3 file" # Ensure there's a tag if audiofile.tag is None: audiofile.initTag() # Update the metadata audiofile.tag.title = title audiofile.tag.artist = artist audiofile.tag.album = album # Handle year if year: try: audiofile.tag.year = int(year) except ValueError: pass # Handle track number if track_num: try: audiofile.tag.track_num = int(track_num) except ValueError: pass # Handle genre if genre: audiofile.tag.genre = genre # Handle comments # First remove any existing comments for comment in list(audiofile.tag.comments): audiofile.tag.comments.remove(comment.description) if comments: audiofile.tag.comments.set(comments, description="", lang="eng") # Remove cover art if requested if remove_cover_art_option: for img in list(audiofile.tag.images): audiofile.tag.images.remove(img.description) else: # Handle cover art if new art is uploaded if cover_art is not None and isinstance(cover_art, dict) and "name" in cover_art: # Remove existing cover art for img in list(audiofile.tag.images): audiofile.tag.images.remove(img.description) # Add new cover art with open(cover_art["name"], "rb") as img_file: img_data = img_file.read() mime_type = "image/jpeg" if cover_art["name"].lower().endswith((".jpg", ".jpeg")) else "image/png" audiofile.tag.images.set(3, img_data, mime_type, "Front cover") # Save the changes audiofile.tag.save() # Create a new file name for the download output_dir = tempfile.mkdtemp() filename = os.path.basename(file_path) if not filename.endswith('.mp3'): filename += '.mp3' output_path = os.path.join(output_dir, f"edited_{filename}") shutil.copy2(file_path, output_path) # Return the updated file for download return output_path, "Changes saved successfully!" except Exception as e: print(f"Error saving changes: {str(e)}") return None, f"Error saving changes: {str(e)}" # Function to handle the cover art upload def update_cover_art(cover_art_file): if cover_art_file is None: return None return cover_art_file # Create the Gradio interface with gr.Blocks(title="MP3 Tag Editor") as app: gr.Markdown("# MP3 Tag Editor") gr.Markdown("Upload an MP3 file to edit its metadata tags and album art.") # Store file path (hidden from UI) file_path = gr.State(None) with gr.Row(): with gr.Column(scale=1): file_input = gr.File(label="Upload MP3 File", file_types=[".mp3"]) load_button = gr.Button("Load MP3") with gr.Column(): gr.Markdown("### Album Art") album_art_display = gr.Image(label="Current Album Art", type="pil") cover_art_input = gr.File(label="Upload New Album Art", file_types=["image"]) remove_cover_art = gr.Checkbox(label="Remove cover image completely?") with gr.Column(scale=2): file_name = gr.Textbox(label="File Name", interactive=False) title_input = gr.Textbox(label="Title") artist_input = gr.Textbox(label="Artist") album_input = gr.Textbox(label="Album") year_input = gr.Textbox(label="Year") track_input = gr.Textbox(label="Track Number") genre_input = gr.Textbox(label="Genre") comments_input = gr.Textbox(label="Comments", lines=3) save_button = gr.Button("Save Changes") download_btn = gr.File(label="Download Modified MP3") status_msg = gr.Textbox(label="Status", interactive=False) # Event handlers load_result = load_button.click( load_mp3, inputs=[file_input], outputs=[ file_path, file_name, title_input, artist_input, album_input, year_input, track_input, genre_input, comments_input, album_art_display ] ) cover_art_input.change( update_cover_art, inputs=[cover_art_input], outputs=[cover_art_input] ) save_button.click( save_changes, inputs=[ file_path, title_input, artist_input, album_input, year_input, track_input, genre_input, comments_input, cover_art_input, remove_cover_art ], outputs=[download_btn, status_msg] ) # Provide instructions gr.Markdown(""" ## How to Use 1. Upload an MP3 file 2. Click "Load MP3" to extract current metadata 3. Edit the metadata fields 4. Optionally upload new album art or check 'Remove cover image completely?' 5. Click "Save Changes" to apply your edits 6. Download the modified MP3 file ## Notes - All processing happens in your browser - Your files are not stored permanently on the server - Original audio quality is preserved """) # Launch the app if __name__ == "__main__": app.launch()