Restructure the dashboard into a two-column layout: a wide main column
holding the new Playlist Overview card, and a narrow (220px) right
sidebar with the compacted Quick Actions and Workflow Guide cards.
The Overview card answers "which playlist should I add media to, and
who will see it?" without opening each playlist. Per playlist it shows:
- name, linked to that playlist's content manager
- item count, total duration and current version
- "N/M online" coverage, or a "no players" warning
- one chip per assigned player, with an online/offline dot, linking
to that player's manage page
- an explicit empty state when no player is assigned
Players with no playlist are surfaced in a separate warning, since they
silently display nothing.
main.py builds this in a fixed number of queries: players are fetched
once and grouped by playlist_id, rather than calling
Playlist.players per row (which would be one query per playlist).
Layout notes:
- minmax(0, 1fr) on the main column so long names cannot widen the grid
- sidebar is sticky on tall screens and collapses below the main column
under 900px, where the two small cards sit side by side
- sidebar cards used the global .card hover lift; suppressed for these
Also fixes a corrupted emoji (U+FFFD) in the System Status card.
Verified at 1600/1200/950/850/600px: no overflow, no button label
wrapping, and correct dark-mode colours.
base.html passed the cache-busting query string INSIDE the filename argument:
url_for('static', filename='uploads/header_logo.png?v=1')
Flask percent-encodes the '?' to '%3F', producing a request for a file
literally named "header_logo.png%3Fv=1", which does not exist. Every
authenticated page therefore logged:
GET /static/uploads/header_logo.png%3Fv=1 404
The logo silently stayed hidden too, because the element carries
onerror="this.style.display='none'".
Changes:
- base.html / login.html: append "?v=..." OUTSIDE url_for(), matching what
admin/customize_logos.html already did correctly.
- Inject logo_version from a context processor instead of hardcoding it.
It is derived from the newest logo's mtime, so uploading a new logo in
Admin -> Customize Logos is reflected immediately. The previous value was
a literal 1, which defeated cache-busting entirely; login.html used
range(1, 999999) | random, which changed on every request and made the
logo uncacheable.
Verified: /, /content/ and /players/ now render
/static/uploads/header_logo.png?v=<mtime> (HTTP 200), and no %3F requests
appear in the logs.
Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
player_page) and the related group/Template references
Result: 6 blueprints, 82 routes, no dead modules or orphan templates.
Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py (referenced by deploy.sh, migrate_network.sh,
docker-entrypoint.sh)
- Caddyfile.example (seeded by deploy.sh; its absence aborts deploy)
Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.
Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
docs/old_code_documentation/) on disk but out of the repo
Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
- api.py: include 'audio' (on/off) field in playlist JSON served to players,
derived from the muted toggle in the manage playlist page
- players.py: include 'audio' field in legacy player playlist builder
- api.py: auto-flip deployment_status pending/deploying -> deployed when a
player sends feedback (player is confirmed running)
- players_list.html: poll deployment status for pending/failed badges so the
players list live-updates once a player starts sending feedback
- .dockerignore: exclude runtime data/ volume dir from build context
- New route GET /players/<id>/edited-media-report with tabular report
- New template edited_media_report.html with User/Filename/Date/Link columns
- Launch Report button (green) next to View All Edited Media button
- Fix: on first edit, move original file to versionized folder as original_<name>
- Fix: content lookup fallback via PlayerEdit records and path regex
- Keep Content.filename pointing to latest edit for player playlist
- Content model: add url column + is_weblink() for web page content items
- Player model: add deployment tracking fields (status, timestamps, message)
- Content blueprint: add add_weblink and add_weblink_to_playlist routes;
weblinks auto-deleted when removed from playlist
- Players blueprint: add SSH deploy mode in add_player; weblink-aware playlist
- API blueprint: weblink URL served directly in playlist response;
add /api/deploy/test-ssh and /api/deploy/player endpoints
- Admin blueprint: add build-player page (clone from Gitea, write base config);
replace nginx status card with Caddy status on HTTPS config page
- caddy_manager: rewritten to generate proper HTTPS/internal-CA Caddyfiles
and reload Caddy via admin API (/load) for live config updates
- ssh_deploy, background_tasks, player_build: new utils for SSH deployment
- background_tasks: push Flask app context into background thread so DB
updates after deployment complete correctly
- ssh_deploy: robust install script detection with passwordless sudo injection
(uses SSH credentials, cleaned up after install)
- Dockerfile: add git, sshpass, openssh-client, rsync
- docker-compose: switch nginx to Caddy on ports 80/443; add port 5000 for dev
- Templates: add_player deploy mode UI, weblink form in playlist/upload pages,
build_player admin page, Caddy status on HTTPS config page
- Migrations: add_url_to_content, add_deployment_fields_to_player
- app.py: call db.create_all() on startup for schema bootstrap
- config.py: add PLAYER_CODE_DIR and PLAYER_REPO_URL settings
- Initialize playlist variable to None to prevent UnboundLocalError
- Fixes crash when player has no assigned playlist
- Ensures graceful handling of all playlist assignment scenarios
- Create new endpoint to handle duration updates for playlist content items
- Validates duration is at least 1 second
- Updates duration in playlist_content association table
- Increments playlist version for sync tracking
- Fixes issue where duration spinner buttons were returning save errors
- Implement Docker image-based deployment (Option 1)
* Code immutable in image, no volume override
* Eliminated init-data.sh manual step
* Simplified deployment process
- Unified persistence in data/ folder
* Moved nginx.conf and nginx-custom-domains.conf to data/
* All runtime configs and data in single location
* Clear separation: repo (source) vs data/ (runtime)
- Archive legacy features
* Groups blueprint and templates removed
* Legacy playlist routes redirected to content area
* Organized in old_code_documentation/
- Added network migration support
* New migrate_network.sh script for IP changes
* Regenerates SSL certs for new IP
* Updates database configuration
* Tested workflow: clone → deploy → migrate
- Enhanced deploy.sh
* Creates data directories
* Copies nginx configs from repo to data/
* Validates file existence before deployment
* Prevents incomplete deployments
- Updated documentation
* QUICK_DEPLOYMENT.md shows 4-step workflow
* Complete deployment workflow documented
* Migration procedures included
- Production ready deployment workflow:
1. Clone & setup (.env configuration)
2. Deploy (./deploy.sh)
3. Migrate network (./migrate_network.sh if needed)
4. Normal operations (docker compose restart)
- Changed ownership of all files to scheianu:scheianu
- Set directories to 755 permissions (rwxr-xr-x)
- Set files to 644 permissions (rw-r--r--)
- Made shell scripts executable (755)
- Allows development without requiring sudo for file modifications
- Improves development workflow and security
- Detect actual HTTPS status from current request (scheme + X-Forwarded-Proto)
- Auto-sync database when HTTPS status mismatch is detected
- Show real-time connection info (protocol, host, port)
- Display helpful message when accessing via HTTPS but config shows disabled
- Add CSS styling for detection status display
- Users now see accurate HTTPS status even if database wasn't in sync
- Added auto-creation of PlayerUser records from player metadata (user_card_data)
- Fixed player_user table schema (removed player_id, made user_code unique globally)
- Created admin page for managing editing users (view, update names, delete)
- Updated permissions: normal users can now access admin panel, editing users, and leftover media
- Admin-only access: user management, system dependencies, logo customization
- Fixed edited media workflow to preserve original files
- Content.filename now points to edited_media folder, keeping originals intact
- Added user display names in edited media page (shows name if set, code otherwise)
- Fixed leftover media file size calculation (handle None values)
- Split editing users into separate card on admin panel with description
- Optimized delete modal for light/dark modes with modern gradients
- Added edit_count tracking to media library with warnings in delete confirmation
- Enhanced PlayerEdit model with CASCADE delete on foreign keys
- Improved player management page to show latest 3 edited files with image previews
- Created new edited_media route and template with full version history
- Implemented horizontal expandable cards with two-column layout
- Added interactive version selection with thumbnail grid
- Included original file in versions list with source badge
- Fixed deletion workflow to clean up PlayerEdit records and edited_media folders
- Enhanced UI with smooth animations, hover effects, and dark mode support
- Added PlayerEdit model to track edited media history
- Created /api/player-edit-media endpoint for receiving edited files from players
- Implemented versioned storage: edited_media/<content_id>/<filename_vN.ext>
- Automatic playlist update when edited media is received
- Updated content.filename to reference versioned file in playlist
- Added 'Edited Media on the Player' card to player management page
- UI shows version history grouped by original file
- Each edit preserves previous versions in archive folder
- Includes dark mode support for new UI elements
- Documentation: PLAYER_EDIT_MEDIA_API.md
- Added edit_on_player_enabled column to playlist_content table
- Updated playlist model to track edit enablement per content item
- Added UI checkbox on upload media page to enable/disable editing
- Added toggle column on manage playlist page for existing content
- Updated API endpoint to return edit_on_player_enabled flag to players
- Fixed docker-entrypoint.sh to use production config
- Supports PDF, Images, and PPTX content types
Features:
- Dockerfile for containerized deployment
- Docker Compose configuration with health checks
- Automated database initialization via entrypoint script
- Quick start script for easy Docker deployment
- Comprehensive Docker deployment documentation (DOCKER.md)
- Complete README with installation and usage instructions
- .dockerignore for optimized image builds
Improvements:
- PDF conversion now preserves orientation (portrait/landscape)
- PDF rendering at 300 DPI for sharp quality
- Maintains aspect ratio during conversion
- Compact media library view with image thumbnails
- Better media preview with scrollable gallery
Docker Features:
- Multi-stage build for smaller images
- Non-root user for security
- Health checks for container monitoring
- Volume mounts for persistent data
- Production-ready Gunicorn configuration
- Support for Redis caching (optional)
Features added:
- Leftover media management page in admin panel with delete functionality for images and videos
- Individual file delete buttons for leftover media
- PDF to PNG conversion (each page becomes a separate image at Full HD resolution)
- Delete functionality for leftover video files
- Enhanced playlist management with duration editing for all content types
- Improved dark mode support for playlist management page
- Content type badges with color coding
- Better styling for duration input fields with save functionality
- Fixed URL generation for duration update endpoint
- Added comprehensive dark mode styling to all pages:
* Dashboard (workflow guide, secondary text, log items)
* Admin panel with user management system
* Content/playlist management page
* Upload media page
* Add player page
- Implemented user management system:
* Create/edit/delete users
* Two role types (user/admin)
* Reset password functionality
* Role-based permissions
- Replaced group assignment with playlist assignment:
* Players now directly assigned to playlists
* Updated add player form and backend
* Removed group selection from player creation
- Fixed bugs:
* Updated instance_path configuration for SQLite
* Fixed import path in app factory
* Updated dependencies (Pillow 11.0.0, removed gevent)
- Added start.sh script for easy development server launch
- Created 10 SVG icon files in app/static/icons/ (Feather Icons style)
- Updated base.html with SVG icons in navigation and dark mode toggle
- Updated dashboard.html with icons in stats cards and quick actions
- Updated content_list_new.html (playlist management) with SVG icons
- Updated upload_media.html with upload-related icons
- Updated manage_player.html with player management icons
- Icons use currentColor for automatic theme adaptation
- Removed emoji dependency for better Raspberry Pi compatibility
- Added ICON_INTEGRATION.md documentation
Templates (8 basic templates):
- base.html: Main layout with navigation, flash messages, responsive design
- login.html: User login form with remember me option
- register.html: User registration with validation
- dashboard.html: Main dashboard with statistics cards and recent activity
- players_list.html: Players list placeholder
- add_player.html: Add player form
- groups_list.html: Groups list placeholder
- create_group.html: Create group form
- content_list.html: Content list placeholder
- upload_content.html: File upload form
- admin.html: Admin panel with system overview
Development Setup:
- run_dev.sh: Automated development server setup script
- Creates virtual environment
- Installs dependencies
- Initializes database
- Creates default admin user (admin/admin123)
- Runs Flask development server on port 5000
Static Files:
- static/uploads/ directory with .gitkeep
- Ready for media file uploads
Testing Features:
✅ Basic navigation and routing
✅ Authentication flow (login/register/logout)
✅ Dashboard with statistics
✅ Flash message system
✅ Responsive design with clean UI
✅ Placeholder templates for all routes
Ready for manual testing at http://localhost:5000
Models (6 + 1 association table):
- User: Authentication with bcrypt, admin role check, last_login tracking
- Player: Digital signage devices with auth codes, status tracking, online detection
- Group: Player/content organization with statistics properties
- Content: Media files with type detection, file size helpers, position ordering
- ServerLog: Audit trail with class methods for logging levels
- PlayerFeedback: Player status updates with error tracking
- group_content: Many-to-many association table for groups and content
Model improvements:
- Added type hints to all methods and properties
- Added database indexes on frequently queried columns (username, auth_code, group_id, player_id, position, level, timestamp, status)
- Added comprehensive docstrings
- Added helper properties (is_online, is_admin, file_size_mb, etc.)
- Added relationship back_populates for bidirectional navigation
- Added timestamps (created_at, updated_at, last_seen, uploaded_at)
Utils (4 modules):
- logger.py: Logging utility with level-based functions (info, warning, error)
- uploads.py: File upload handling with progress tracking, video optimization
- group_player_management.py: Player/group status tracking and bulk operations
- pptx_converter.py: PowerPoint to PDF conversion using LibreOffice
Utils improvements:
- Full type hints on all functions
- Comprehensive error handling
- Progress tracking for long-running operations
- Video optimization (H.264, 30fps, max 1080p, 8Mbps)
- Helper functions for time formatting and statistics
- Proper logging of all operations
Performance optimizations:
- Database indexes on all foreign keys and frequently filtered columns
- Lazy loading for relationships where appropriate
- Efficient queries with proper ordering
- Helper properties to avoid repeated calculations
Ready for template migration and testing