188 lines
7.4 KiB
Python
Executable File
188 lines
7.4 KiB
Python
Executable File
#!/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()
|