Created
December 31, 2025 18:18
-
-
Save mrichman/b5b0ee7bf84d51592ab93c3a9cc38e9d to your computer and use it in GitHub Desktop.
The script detects when you rename a file's extension (e.g., right-click → rename data.xlsx to data.csv) and converts the content to match.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import time | |
| import shutil | |
| from pathlib import Path | |
| from watchdog.observers import Observer | |
| from watchdog.events import FileSystemEventHandler | |
| import pandas as pd | |
| class ExtensionChangeHandler(FileSystemEventHandler): | |
| def __init__(self, watch_dir): | |
| self.watch_dir = Path(watch_dir) | |
| self.processing = set() | |
| def on_moved(self, event): | |
| """Triggered when a file is renamed (extension changed)""" | |
| if event.is_directory: | |
| return | |
| src_path = Path(event.src_path) | |
| dest_path = Path(event.dest_path) | |
| # Check if only extension changed (same base name) | |
| if src_path.stem == dest_path.stem and src_path.suffix != dest_path.suffix: | |
| if dest_path in self.processing: | |
| return | |
| self.processing.add(dest_path) | |
| time.sleep(0.2) | |
| self.convert_file(src_path.suffix, dest_path) | |
| self.processing.discard(dest_path) | |
| def convert_file(self, original_ext, target_path): | |
| """Convert file content from original format to target format""" | |
| original_ext = original_ext.lower() | |
| target_ext = target_path.suffix.lower() | |
| # Create backup with original extension | |
| backup_path = target_path.with_suffix(original_ext + '.backup') | |
| try: | |
| # Copy to backup (file still has old content but new extension) | |
| shutil.copy2(target_path, backup_path) | |
| print(f"Converting {target_path.name} from {original_ext} to {target_ext}") | |
| # Read from backup with original format | |
| df = None | |
| if original_ext in ['.xlsx', '.xls']: | |
| df = pd.read_excel(backup_path) | |
| elif original_ext == '.csv': | |
| df = pd.read_csv(backup_path) | |
| elif original_ext == '.json': | |
| df = pd.read_json(backup_path) | |
| elif original_ext == '.txt': | |
| df = pd.read_csv(backup_path, sep='\t') | |
| else: | |
| print(f"✗ Unsupported source format: {original_ext}") | |
| backup_path.unlink() | |
| return | |
| # Write to target with new format | |
| if target_ext == '.csv': | |
| df.to_csv(target_path, index=False) | |
| elif target_ext in ['.xlsx', '.xls']: | |
| df.to_excel(target_path, index=False, engine='openpyxl') | |
| elif target_ext == '.json': | |
| df.to_json(target_path, orient='records', indent=2) | |
| elif target_ext == '.txt': | |
| df.to_csv(target_path, sep='\t', index=False) | |
| else: | |
| print(f"✗ Unsupported target format: {target_ext}") | |
| # Restore original | |
| shutil.move(backup_path, target_path) | |
| return | |
| # Remove backup after successful conversion | |
| backup_path.unlink() | |
| print(f"✓ Converted {target_path.name}") | |
| except Exception as e: | |
| print(f"✗ Error converting {target_path.name}: {str(e)}") | |
| # Restore original file on error | |
| if backup_path.exists(): | |
| shutil.move(backup_path, target_path) | |
| def main(): | |
| watch_directory = "./watched_files" | |
| Path(watch_directory).mkdir(exist_ok=True) | |
| print(f"Watching directory: {watch_directory}") | |
| print("Rename a file's extension to trigger conversion") | |
| print("Supported formats: .xlsx, .xls, .csv, .json, .txt") | |
| print("Press Ctrl+C to stop\n") | |
| event_handler = ExtensionChangeHandler(watch_directory) | |
| observer = Observer() | |
| observer.schedule(event_handler, watch_directory, recursive=False) | |
| observer.start() | |
| try: | |
| while True: | |
| time.sleep(1) | |
| except KeyboardInterrupt: | |
| observer.stop() | |
| print("\nStopped watching") | |
| observer.join() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment