Spaces:
Sleeping
Sleeping
| 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() |