mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
Feature/static image manager (#95)
* feat(static-image): Add static image manager with web interface - Create StaticImageManager class with image scaling and transparency support - Add configuration options for display duration, zoom scale, and background color - Integrate with display controller and web interface - Add image upload functionality to web interface - Support for various image formats with proper scaling - Efficient image processing with aspect ratio preservation - Ready for future scrolling feature implementation * fix(static-image): Move display duration to main display_durations block - Remove display_duration from static_image config section - Update StaticImageManager to read duration from display.display_durations.static_image - Remove display duration field from web interface form - Update web interface JavaScript to not include display_duration in payload - Follows same pattern as all other managers in the project * feat(static-image): Add fit to display option - Add fit_to_display checkbox to automatically scale images to fit display - When enabled, images are scaled to fit display dimensions while preserving aspect ratio - When disabled, manual zoom_scale control is available - Update web interface with smart form controls (zoom scale disabled when fit to display is on) - Prevents stretching or cropping - images are always properly fitted - Default to fit_to_display=true for better user experience * refactor(static-image): Remove zoom_scale and simplify to fit_to_display only - Remove zoom_scale option entirely as it was confusing and redundant - Simplify image processing to always fit to display dimensions - Remove zoom_scale field from web interface - Clean up JavaScript to remove zoom scale logic - Images are now always properly fitted without stretching or cropping - Much simpler and more intuitive user experience
This commit is contained in:
@@ -20,7 +20,9 @@ from src.odds_ticker_manager import OddsTickerManager
|
||||
from src.calendar_manager import CalendarManager
|
||||
from src.youtube_display import YouTubeDisplay
|
||||
from src.text_display import TextDisplay
|
||||
from src.static_image_manager import StaticImageManager
|
||||
from src.news_manager import NewsManager
|
||||
from werkzeug.utils import secure_filename
|
||||
from src.nhl_managers import NHLLiveManager, NHLRecentManager, NHLUpcomingManager
|
||||
from src.nba_managers import NBALiveManager, NBARecentManager, NBAUpcomingManager
|
||||
from src.mlb_manager import MLBLiveManager, MLBRecentManager, MLBUpcomingManager
|
||||
@@ -421,6 +423,10 @@ class OnDemandRunner:
|
||||
mgr = TextDisplay(display_manager, cfg)
|
||||
self._force_enable(mgr)
|
||||
return mgr, lambda fc=False: mgr.display(), lambda: getattr(mgr, 'update', lambda: None)(), 5.0
|
||||
if mode == 'static_image':
|
||||
mgr = StaticImageManager(display_manager, cfg)
|
||||
self._force_enable(mgr)
|
||||
return mgr, lambda fc=False: mgr.display(force_clear=fc), lambda: mgr.update(), float(cfg.get('display', {}).get('display_durations', {}).get('static_image', 10))
|
||||
if mode == 'of_the_day':
|
||||
from src.of_the_day_manager import OfTheDayManager # local import to avoid circulars
|
||||
mgr = OfTheDayManager(display_manager, cfg)
|
||||
@@ -1594,6 +1600,42 @@ def get_current_display():
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e), 'image': None}), 500
|
||||
|
||||
@app.route('/upload_image', methods=['POST'])
|
||||
def upload_image():
|
||||
"""Upload an image for static image display."""
|
||||
try:
|
||||
if 'image' not in request.files:
|
||||
return jsonify({'success': False, 'error': 'No image file provided'})
|
||||
|
||||
file = request.files['image']
|
||||
if file.filename == '':
|
||||
return jsonify({'success': False, 'error': 'No image file selected'})
|
||||
|
||||
if file:
|
||||
# Secure the filename
|
||||
filename = secure_filename(file.filename)
|
||||
# Ensure we have a valid extension
|
||||
if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
|
||||
return jsonify({'success': False, 'error': 'Invalid file type. Only image files are allowed.'})
|
||||
|
||||
# Create the static images directory if it doesn't exist
|
||||
static_images_dir = os.path.join(os.path.dirname(__file__), 'assets', 'static_images')
|
||||
os.makedirs(static_images_dir, exist_ok=True)
|
||||
|
||||
# Save the file
|
||||
file_path = os.path.join(static_images_dir, filename)
|
||||
file.save(file_path)
|
||||
|
||||
# Return the relative path for the config
|
||||
relative_path = f"assets/static_images/{filename}"
|
||||
|
||||
logger.info(f"Image uploaded successfully: {relative_path}")
|
||||
return jsonify({'success': True, 'path': relative_path})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error uploading image: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
|
||||
@app.route('/api/editor/layouts', methods=['GET'])
|
||||
def get_custom_layouts():
|
||||
"""Return saved custom layouts for the editor if available."""
|
||||
|
||||
Reference in New Issue
Block a user