├── .gitignore ├── requirements.txt ├── LICENSE ├── README.md └── ipynb_converter_app.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the virtual environment directory 2 | venv/ -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | ipynb-py-convert 3 | requests 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 rohanmistry231 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jupyter Notebook to Python Converter 2 | 3 | A web application built with Streamlit that allows users to easily convert Jupyter Notebook (.ipynb) files to Python (.py) script files. 4 | 5 | ## Features 6 | 7 | - **User-friendly Interface**: Clean and intuitive Streamlit UI 8 | - **Multiple Input Methods**: 9 | - Upload local .ipynb files directly 10 | - Convert from URLs 11 | - **Batch Processing**: Convert multiple files at once 12 | - **Download Options**: 13 | - Direct download for single files 14 | - ZIP archive for multiple files 15 | - **Error Handling**: Clear feedback on conversion success or failure 16 | 17 | ## Requirements 18 | 19 | - Python 3.7 or higher 20 | - Streamlit 21 | - ipynb-py-convert 22 | - requests 23 | 24 | ## Installation 25 | 26 | 1. Clone this repository: 27 | ``` 28 | git clone https://github.com/yourusername/ipynb-to-py-converter.git 29 | cd ipynb-to-py-converter 30 | ``` 31 | 32 | 2. Install the required packages: 33 | ``` 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | ## Usage 38 | 39 | 1. Run the Streamlit app: 40 | ``` 41 | streamlit run ipynb_converter_app.py 42 | ``` 43 | 44 | 2. Open your web browser and navigate to: 45 | ``` 46 | http://localhost:8501 47 | ``` 48 | 49 | 3. Use either the "File Upload" or "URL Input" tab to convert your Jupyter Notebook files 50 | 51 | ## How It Works 52 | 53 | This application uses the `ipynb-py-convert` library to perform the conversion from .ipynb to .py format. The conversion preserves: 54 | 55 | - Code cells 56 | - Markdown cells (as comments) 57 | - Cell execution order 58 | 59 | ## Project Structure 60 | 61 | ``` 62 | ipynb-to-py-converter/ 63 | ├── ipynb_converter_app.py # Main Streamlit application 64 | ├── requirements.txt # Python dependencies 65 | └── README.md # Project documentation 66 | ``` 67 | 68 | ## Deployment 69 | 70 | This application can be deployed to Streamlit Sharing, Heroku, or any other platform that supports Streamlit applications. 71 | 72 | ## License 73 | 74 | This project is licensed under the MIT License - see the LICENSE file for details. 75 | 76 | ## Acknowledgements 77 | 78 | - [ipynb-py-convert](https://github.com/kiwi0fruit/ipynb-py-convert) for the conversion tool 79 | - [Streamlit](https://streamlit.io/) for the web application framework 80 | -------------------------------------------------------------------------------- /ipynb_converter_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import os 3 | import tempfile 4 | import zipfile 5 | import subprocess 6 | import uuid 7 | import base64 8 | import requests 9 | from pathlib import Path 10 | from urllib.parse import urlparse 11 | 12 | st.set_page_config( 13 | page_title="Jupyter Notebook to Python Converter", 14 | page_icon="🐍", 15 | layout="wide", 16 | ) 17 | 18 | # Custom CSS 19 | st.markdown(""" 20 | 59 | """, unsafe_allow_html=True) 60 | 61 | def check_ipynb_py_convert(): 62 | """Check if ipynb-py-convert is installed, install if not.""" 63 | try: 64 | subprocess.run(["ipynb-py-convert", "--help"], capture_output=True, text=True) 65 | return True 66 | except FileNotFoundError: 67 | try: 68 | st.info("Installing ipynb-py-convert...") 69 | subprocess.run(["pip", "install", "ipynb-py-convert"], check=True) 70 | return True 71 | except subprocess.CalledProcessError: 72 | st.error("Failed to install ipynb-py-convert. Please make sure you have pip installed and try again.") 73 | return False 74 | 75 | def convert_ipynb_to_py(input_path, output_path): 76 | """Convert .ipynb file to .py file using ipynb-py-convert.""" 77 | try: 78 | result = subprocess.run( 79 | ["ipynb-py-convert", input_path, output_path], 80 | capture_output=True, 81 | text=True, 82 | check=True 83 | ) 84 | return True, result.stdout 85 | except subprocess.CalledProcessError as e: 86 | return False, f"Error converting file: {e.stderr}" 87 | 88 | def download_file_from_url(url, temp_dir): 89 | """Download a file from URL and save it to a temporary directory.""" 90 | try: 91 | response = requests.get(url, stream=True) 92 | response.raise_for_status() 93 | 94 | # Get filename from URL or generate one if not available 95 | parsed_url = urlparse(url) 96 | filename = os.path.basename(parsed_url.path) 97 | if not filename or '.' not in filename: 98 | filename = f"downloaded_notebook_{uuid.uuid4().hex}.ipynb" 99 | elif not filename.endswith('.ipynb'): 100 | filename = f"{filename}.ipynb" 101 | 102 | file_path = os.path.join(temp_dir, filename) 103 | 104 | with open(file_path, 'wb') as f: 105 | for chunk in response.iter_content(chunk_size=8192): 106 | f.write(chunk) 107 | 108 | return True, file_path, filename 109 | except Exception as e: 110 | return False, str(e), None 111 | 112 | def create_download_link(file_path, filename): 113 | """Create a download link for the converted file.""" 114 | with open(file_path, "rb") as file: 115 | contents = file.read() 116 | b64 = base64.b64encode(contents).decode() 117 | href = f'Download Converted File' 118 | return href 119 | 120 | def create_zip_download_link(zip_path): 121 | """Create a download link for the zip file containing multiple converted files.""" 122 | with open(zip_path, "rb") as file: 123 | contents = file.read() 124 | b64 = base64.b64encode(contents).decode() 125 | href = f'Download All Converted Files' 126 | return href 127 | 128 | def main(): 129 | st.markdown('

🐍 Jupyter Notebook to Python Converter

', unsafe_allow_html=True) 130 | 131 | st.markdown('
Convert your .ipynb Jupyter Notebook files to .py Python files with ease! Upload files directly or provide URLs.
', unsafe_allow_html=True) 132 | 133 | if not check_ipynb_py_convert(): 134 | st.stop() 135 | 136 | tab1, tab2 = st.tabs(["File Upload", "URL Input"]) 137 | 138 | with tab1: 139 | st.markdown('

Upload Jupyter Notebook Files

', unsafe_allow_html=True) 140 | uploaded_files = st.file_uploader("Choose .ipynb files", type=["ipynb"], accept_multiple_files=True) 141 | 142 | if uploaded_files: 143 | with st.form("process_uploaded_files_form"): 144 | st.write("Files to convert:") 145 | for file in uploaded_files: 146 | st.write(f"- {file.name}") 147 | 148 | submit_button = st.form_submit_button("Convert Files") 149 | 150 | if submit_button: 151 | with tempfile.TemporaryDirectory() as temp_dir: 152 | # Process each uploaded file 153 | success_count = 0 154 | failed_count = 0 155 | converted_files = [] 156 | 157 | with st.spinner("Converting files..."): 158 | for uploaded_file in uploaded_files: 159 | # Save the uploaded file to temp directory 160 | input_path = os.path.join(temp_dir, uploaded_file.name) 161 | with open(input_path, "wb") as f: 162 | f.write(uploaded_file.getbuffer()) 163 | 164 | # Create output path 165 | output_filename = os.path.splitext(uploaded_file.name)[0] + ".py" 166 | output_path = os.path.join(temp_dir, output_filename) 167 | 168 | # Convert the file 169 | success, message = convert_ipynb_to_py(input_path, output_path) 170 | 171 | if success: 172 | converted_files.append((output_path, output_filename)) 173 | success_count += 1 174 | st.success(f"Successfully converted {uploaded_file.name} to {output_filename}") 175 | else: 176 | failed_count += 1 177 | st.error(f"Failed to convert {uploaded_file.name}: {message}") 178 | 179 | # Create summary 180 | st.write(f"Conversion complete: {success_count} succeeded, {failed_count} failed") 181 | 182 | # If files were converted, create a ZIP file and download link 183 | if converted_files: 184 | if len(converted_files) == 1: 185 | # If only one file, provide direct download 186 | output_path, output_filename = converted_files[0] 187 | st.markdown(create_download_link(output_path, output_filename), unsafe_allow_html=True) 188 | else: 189 | # For multiple files, create a zip 190 | zip_path = os.path.join(temp_dir, "converted_files.zip") 191 | with zipfile.ZipFile(zip_path, 'w') as zipf: 192 | for file_path, file_name in converted_files: 193 | zipf.write(file_path, arcname=file_name) 194 | 195 | st.markdown(create_zip_download_link(zip_path), unsafe_allow_html=True) 196 | 197 | with tab2: 198 | st.markdown('

Convert from URLs

', unsafe_allow_html=True) 199 | 200 | with st.form("url_input_form"): 201 | url_input = st.text_area("Enter URLs (one per line) of .ipynb files to convert:", height=150) 202 | submit_url_button = st.form_submit_button("Convert From URLs") 203 | 204 | if submit_url_button and url_input: 205 | urls = [url.strip() for url in url_input.split('\n') if url.strip()] 206 | 207 | if not urls: 208 | st.error("Please enter at least one valid URL.") 209 | else: 210 | with tempfile.TemporaryDirectory() as temp_dir: 211 | # Process each URL 212 | success_count = 0 213 | failed_count = 0 214 | converted_files = [] 215 | 216 | with st.spinner("Downloading and converting files..."): 217 | for url in urls: 218 | # Download the file 219 | download_success, download_result, filename = download_file_from_url(url, temp_dir) 220 | 221 | if download_success: 222 | input_path = download_result 223 | output_filename = os.path.splitext(filename)[0] + ".py" 224 | output_path = os.path.join(temp_dir, output_filename) 225 | 226 | # Convert the file 227 | convert_success, message = convert_ipynb_to_py(input_path, output_path) 228 | 229 | if convert_success: 230 | converted_files.append((output_path, output_filename)) 231 | success_count += 1 232 | st.success(f"Successfully converted file from {url} to {output_filename}") 233 | else: 234 | failed_count += 1 235 | st.error(f"Failed to convert file from {url}: {message}") 236 | else: 237 | failed_count += 1 238 | st.error(f"Failed to download file from {url}: {download_result}") 239 | 240 | # Create summary 241 | st.write(f"Conversion complete: {success_count} succeeded, {failed_count} failed") 242 | 243 | # If files were converted, create a ZIP file and download link 244 | if converted_files: 245 | if len(converted_files) == 1: 246 | # If only one file, provide direct download 247 | output_path, output_filename = converted_files[0] 248 | st.markdown(create_download_link(output_path, output_filename), unsafe_allow_html=True) 249 | else: 250 | # For multiple files, create a zip 251 | zip_path = os.path.join(temp_dir, "converted_files.zip") 252 | with zipfile.ZipFile(zip_path, 'w') as zipf: 253 | for file_path, file_name in converted_files: 254 | zipf.write(file_path, arcname=file_name) 255 | 256 | st.markdown(create_zip_download_link(zip_path), unsafe_allow_html=True) 257 | 258 | # Usage Instructions Section 259 | st.markdown("---") 260 | st.markdown('

How to Use

', unsafe_allow_html=True) 261 | 262 | with st.expander("Usage Instructions", expanded=False): 263 | st.markdown(""" 264 | ### File Upload Tab: 265 | 1. Click "Browse files" to select one or more .ipynb files from your computer 266 | 2. Click "Convert Files" to process them 267 | 3. Download the converted .py file(s) 268 | 269 | ### URL Input Tab: 270 | 1. Paste URLs of .ipynb files (one per line) 271 | 2. Click "Convert From URLs" to download and process them 272 | 3. Download the converted .py file(s) 273 | 274 | ### Notes: 275 | - Multiple files will be combined into a single ZIP archive for download 276 | - The app needs internet access to install dependencies and download files from URLs 277 | """) 278 | 279 | # About Section 280 | st.markdown("---") 281 | with st.expander("About This App", expanded=False): 282 | st.markdown(""" 283 | This app uses the `ipynb-py-convert` library to convert Jupyter Notebooks (.ipynb) to Python script files (.py). 284 | 285 | Features: 286 | - Convert from local file uploads 287 | - Convert from URLs 288 | - Handle multiple files simultaneously 289 | - Provide downloadable results 290 | 291 | The conversion preserves code cells, markdown cells, and comments in the resulting Python scripts. 292 | """) 293 | 294 | st.markdown('', unsafe_allow_html=True) 295 | 296 | if __name__ == "__main__": 297 | main() 298 | --------------------------------------------------------------------------------