A modern, full-featured digital signage solution built with React and Node.js that automatically synchronizes and displays media content from Google Drive. Perfect for information displays, digital signage, and automated presentations.
- πΌοΈ Seamless display of images and videos from Google Drive
- β‘ Real-time updates via WebSocket connections
- π¦ Intelligent caching system for optimal performance
- π Automatic content synchronization with change detection
- π Advanced scheduling with vacation periods and daily time windows
- β Configurable display times and operating days
- π― Date-based content targeting through filename parsing
- π± Touch and swipe support for navigation
- π₯οΈ Full kiosk mode support
- π¨ Smooth transitions and animations
- π Dynamic day/night mode transitions
- π‘οΈ Live weather updates and animations
- π NASA Astronomy Picture of the Day integration
- π Health monitoring and automatic recovery
- π Secure operation with minimal dependencies
The application uses WebSocket connections to provide real-time updates:
- Instant content updates when files change in Google Drive
- Live schedule status synchronization
- Immediate system health notifications
- Automatic reconnection handling
- Reduced server load compared to polling
Multi-level caching strategy for optimal performance:
-
Browser Cache:
- Media files cached with appropriate headers
- Conditional requests using ETags
- Cache invalidation on content updates
-
Server-Side Cache:
- Efficient media file storage
- Metadata caching for quick access
- Automatic cache cleanup
- Node.js (v18.0.0 or higher)
- npm (v8.0.0 or higher)
- A Google Cloud Platform account
- For kiosk mode: Debian-based Linux system (e.g., Raspberry Pi OS)
- WebSocket-capable browser
terminal-slide-show/
βββ client/ # Frontend React application
β βββ public/ # Static assets
β βββ src/
β β βββ components/ # React components
β β β βββ slideshow/ # Slideshow components
β β β βββ dynamic_daily_view/ # Dynamic view components
β β βββ hooks/ # Custom React hooks
β β β βββ useWebSocket.js # WebSocket connection hook
β β βββ utils/ # Utility functions
β β βββ styles/ # CSS stylesheets
βββ server/ # Backend application
β βββ config/ # Configuration files
β β βββ config.js # Main configuration
β β βββ frontend.config.js # Frontend-specific config
β βββ src/
β β βββ services/ # Core services
β β β βββ websocket/ # WebSocket server
β β β βββ cache/ # Caching service
β β βββ utils/ # Utility functions
β βββ data/ # Local data (quotes, facts)
βββ scripts/ # Shell scripts
βββ downloads/ # Local media storage
# Clone the repository
git clone https://github.com/asta-dhbw/Terminal-Slide-Show.git
cd terminal-slide-show
# Install dependencies
npm install
# Create necessary directories
mkdir -p downloads logs cache
# Create configuration files
cp server/config/config.example.js server/config/config.js
cp server/config/.env.example server/config/.env
You have two options for authenticating with Google Drive:
- Create a new project in Google Cloud Console
- Enable the Google Drive API
- Create API credentials:
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "API Key"
- Copy the API key
- Add the API key to
config.js
:google: { useServiceAccount: false, apiKey: 'YOUR_API_KEY', folderId: 'YOUR_FOLDER_ID' }
- Make your Google Drive folder publicly accessible (with link)
- Create a new project in Google Cloud Console
- Enable the Google Drive API
- Create a Service Account:
- Navigate to "IAM & Admin" > "Service Accounts"
- Click "Create Service Account"
- Grant the role "Drive File Viewer" or necessary permissions
- Create and download JSON key
- Place the downloaded JSON key in
server/config/service-account.json
- Share your Google Drive folder with the service account email
- Configure
config.js
:google: { useServiceAccount: true, serviceAccountPath: './config/service-account.json', folderId: 'YOUR_FOLDER_ID' }
Configure caching behavior in config.js
:
cache: {
// Browser cache settings
browser: {
maxAge: 86400, // Cache lifetime in seconds
revalidate: true, // Enable revalidation
etags: true // Enable ETag support
},
// Server cache settings
server: {
mediaCache: {
maxSize: '1GB', // Maximum cache size
cleanupInterval: '1h' // Cache cleanup interval
},
metadataCache: {
ttl: 300, // Time-to-live in seconds
checkPeriod: 600 // Cleanup check interval
}
}
}
Configure WebSocket behavior in config.js
:
websocket: {
// WebSocket server settings
server: {
port: 3001,
heartbeat: 30000, // Heartbeat interval
reconnectTimeout: 5000 // Client reconnection timeout
},
// Client settings
client: {
reconnectAttempts: 5,
reconnectInterval: 1000,
messageTimeout: 5000
}
}
Files in Google Drive should follow this naming pattern to enable scheduling:
- Single date:
filename_DD-MM-YYYY.ext
- Date range:
filename_DD-MM-YYYY@DD-MM-YYYY.ext
- Short format:
filename_DD-MM@DD-MM.ext
- Custom duration:
filename_Xs.ext
(where X is duration in seconds)
It is possible to also use just YY
and use any of these separators: -._
You can combine both date and duration patterns:
Examples:
banner_01-01-2024@31-01-2024.jpg # Show in January 2024
notice_15-03.jpg # Show on March 15th (any year)
event_01-06@15-06.jpg # Show June 1-15 (any year)
image_5s_25-12-2024.jpg β
(works)
image_10s_.jpg β
(works)
image5s_25-12-2024.jpg β (won't work - missing leading _)
image_5s25-12-2024.jpg β (won't work - missing trailing _)
Configure display times in config.js
:
schedule: {
enabled: true,
onTime: '06:30', // Display start time
offTime: '20:00', // Display end time
days: [1, 2, 3, 4, 5], // Monday to Friday
vacationPeriods: [ // Optional vacation periods
{ start: '24.06.2024', end: '24.07.2024' }
]
}
The application supports the following WebSocket events:
// Client-side subscription
ws.subscribe('media-update', (data) => {
// Handle media updates
});
ws.subscribe('schedule-update', (data) => {
// Handle schedule changes
});
ws.subscribe('system-health', (data) => {
// Handle system health updates
});
The application sets appropriate cache headers for different types of content:
// Example cache headers for media files
{
'Cache-Control': 'public, max-age=86400',
'ETag': true,
'Last-Modified': timestamp
}
// Example cache headers for dynamic content
{
'Cache-Control': 'no-cache, must-revalidate',
'ETag': true
}
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the GPL-3.0 License - see the LICENSE file for details.