#!/usr/bin/env python3 """ Media Management Utility for Motorcycle Adventure Community This script provides utilities for managing post media files: - Create missing media folders for existing posts - Clean up orphaned media folders - Migrate files from old structure to new structure - Generate thumbnails for images Usage: python manage_media.py --help """ import os import sys import argparse import shutil from datetime import datetime import uuid # Add the app directory to the path sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'app')) from app import create_app from app.models import Post, PostImage, GPXFile from app.extensions import db def create_missing_media_folders(): """Create media folders for existing posts that don't have them""" app = create_app() with app.app_context(): posts_without_folders = Post.query.filter_by(media_folder=None).all() print(f"Found {len(posts_without_folders)} posts without media folders") for post in posts_without_folders: # Generate a media folder name folder_name = f"post_{uuid.uuid4().hex[:8]}_{post.created_at.strftime('%Y%m%d')}" post.media_folder = folder_name # Create the folder structure media_path = os.path.join(app.root_path, 'static', 'media', 'posts', folder_name) os.makedirs(os.path.join(media_path, 'images'), exist_ok=True) os.makedirs(os.path.join(media_path, 'gpx'), exist_ok=True) print(f"Created media folder for post {post.id}: {folder_name}") db.session.commit() print("Media folders created successfully!") def migrate_old_files(): """Migrate files from old upload structure to new media structure""" app = create_app() with app.app_context(): # Migrate images old_images_path = os.path.join(app.instance_path, 'uploads', 'images') if os.path.exists(old_images_path): print("Migrating image files...") for image in PostImage.query.all(): if image.post.media_folder: old_path = os.path.join(old_images_path, image.filename) new_path = os.path.join(app.root_path, 'static', 'media', 'posts', image.post.media_folder, 'images', image.filename) if os.path.exists(old_path) and not os.path.exists(new_path): os.makedirs(os.path.dirname(new_path), exist_ok=True) shutil.move(old_path, new_path) print(f"Moved image: {image.filename}") # Migrate GPX files old_gpx_path = os.path.join(app.instance_path, 'uploads', 'gpx') if os.path.exists(old_gpx_path): print("Migrating GPX files...") for gpx_file in GPXFile.query.all(): if gpx_file.post.media_folder: old_path = os.path.join(old_gpx_path, gpx_file.filename) new_path = os.path.join(app.root_path, 'static', 'media', 'posts', gpx_file.post.media_folder, 'gpx', gpx_file.filename) if os.path.exists(old_path) and not os.path.exists(new_path): os.makedirs(os.path.dirname(new_path), exist_ok=True) shutil.move(old_path, new_path) print(f"Moved GPX file: {gpx_file.filename}") print("File migration completed!") def clean_orphaned_folders(): """Remove media folders that don't have corresponding posts""" app = create_app() with app.app_context(): media_posts_path = os.path.join(app.root_path, 'static', 'media', 'posts') if not os.path.exists(media_posts_path): print("No media posts directory found") return # Get all folder names from database used_folders = set(post.media_folder for post in Post.query.filter(Post.media_folder.isnot(None)).all()) # Get all actual folders actual_folders = set(name for name in os.listdir(media_posts_path) if os.path.isdir(os.path.join(media_posts_path, name))) # Find orphaned folders orphaned_folders = actual_folders - used_folders if orphaned_folders: print(f"Found {len(orphaned_folders)} orphaned folders:") for folder in orphaned_folders: folder_path = os.path.join(media_posts_path, folder) print(f" {folder}") # Ask for confirmation before deleting response = input(f"Delete folder {folder}? (y/N): ") if response.lower() == 'y': shutil.rmtree(folder_path) print(f" Deleted: {folder}") else: print(f" Skipped: {folder}") else: print("No orphaned folders found") def show_media_stats(): """Show statistics about media storage""" app = create_app() with app.app_context(): total_posts = Post.query.count() posts_with_media_folders = Post.query.filter(Post.media_folder.isnot(None)).count() total_images = PostImage.query.count() total_gpx_files = GPXFile.query.count() print("Media Storage Statistics:") print(f" Total posts: {total_posts}") print(f" Posts with media folders: {posts_with_media_folders}") print(f" Posts without media folders: {total_posts - posts_with_media_folders}") print(f" Total images: {total_images}") print(f" Total GPX files: {total_gpx_files}") # Calculate total storage used media_posts_path = os.path.join(app.root_path, 'static', 'media', 'posts') if os.path.exists(media_posts_path): total_size = 0 for root, dirs, files in os.walk(media_posts_path): for file in files: file_path = os.path.join(root, file) total_size += os.path.getsize(file_path) print(f" Total storage used: {total_size / (1024*1024):.2f} MB") def main(): parser = argparse.ArgumentParser(description='Media Management Utility') parser.add_argument('--create-folders', action='store_true', help='Create missing media folders for existing posts') parser.add_argument('--migrate-files', action='store_true', help='Migrate files from old structure to new structure') parser.add_argument('--clean-orphaned', action='store_true', help='Clean up orphaned media folders') parser.add_argument('--stats', action='store_true', help='Show media storage statistics') parser.add_argument('--all', action='store_true', help='Run all operations (create folders, migrate files)') args = parser.parse_args() if args.all: create_missing_media_folders() migrate_old_files() elif args.create_folders: create_missing_media_folders() elif args.migrate_files: migrate_old_files() elif args.clean_orphaned: clean_orphaned_folders() elif args.stats: show_media_stats() else: parser.print_help() if __name__ == '__main__': main()