A web-based video playback system where multiple users manage playlists via a web interface, with videos played automatically on a dedicated big-screen PC using VLC. The system intelligently balances video priority with fairness across users.
SingleStepViewer is purely wibecoded!!! Now you have been warned!
SingleStepViewer was written for a tiny computer party called "SingleStep" :-)
- ✅ Web Application Infrastructure (ASP.NET Core 9.0 + Blazor Server)
- ✅ Database Layer (EF Core with SQLite, ASP.NET Identity)
- ✅ Core Services (Playlist, Video Download, Queue Management, VLC Playback)
- ✅ Background Services (Playback Engine, Video Downloader)
- ✅ Smart Scheduling Algorithm (Weighted fair queue with priority + fairness)
- ✅ Real-time Updates (SignalR for live playback status)
- ✅ Complete Blazor UI (Dashboard, Playlists, Queue visualization)
- ✅ User Authentication (ASP.NET Identity with roles)
- ✅ Video Download (yt-dlp integration via Process)
- ✅ VLC Integration (LibVLCSharp for playback control)
- .NET 9.0 SDK - Download here
- yt-dlp - Download here
- Place
yt-dlp.exein PATH or updateappsettings.jsonwith full path
- Place
- VLC Media Player - Download here
- Install to default location:
C:\Program Files\VideoLAN\VLC
- Install to default location:
- ffmpeg - Required for merging video+audio streams (enables 1080p+ quality)
- Install via winget:
winget install ffmpeg - Or download from ffmpeg.org
- Install via winget:
- Node.js - Required for yt-dlp to download YouTube videos in original quality
- Install via winget:
winget install OpenJS.NodeJS - Or download from nodejs.org
- Install via winget:
# 1. Clone or navigate to the project directory
cd C:\Users\username\source\repos\SingleStepViewer
# 2. Restore packages (if needed)
dotnet restore
# 3. Build the project
dotnet build
# 4. Run the application
dotnet runThe application will start on https://localhost:5001 (or the configured port).
- Email: admin@singlestep.local
- Password: Admin123!
- Real-time "Now Playing" display
- Queue preview (next 5 videos)
- System statistics (pending/ready counts)
- Automatic updates via SignalR
- Create multiple playlists per user
- Add videos from YouTube and other sources
- Set priority for each video (1-10)
- Automatic metadata extraction (title, duration, thumbnail)
- Background video downloading
The weighted fair queue algorithm balances:
- Priority: User-assigned priority (1-10)
- Fairness: Time since user's last video played
Score Formula:
Score = (Priority × 10) + ((Minutes Since Last Played ÷ 10) × 5)
This ensures:
- High-priority videos get preference
- Users who haven't had videos played recently get a boost
- No single user can dominate the queue
- Video Downloader Service: Downloads pending videos concurrently (max 2 at a time)
- Playback Engine Service: Automatically plays videos from the queue
- SignalR Broadcasting: Real-time updates to all connected clients
- Full-screen playback on dedicated display
- Automatic video transitions
- Volume control
- Event-driven architecture (media ended, errors)
Edit appsettings.json to configure the system:
"ConnectionStrings": {
"DefaultConnection": "Data Source=singlestepviewer.db"
}"Playback": {
"VlcPath": "C:\\Program Files\\VideoLAN\\VLC",
"DefaultVolume": 80,
"EnableFullscreen": true,
"EnableVideoOutput": true
}Note: Set EnableVideoOutput: false for development/testing without VLC window.
"Scheduling": {
"PriorityWeight": 10.0,
"FairnessWeight": 5.0,
"UserCooldownMinutes": 15,
"QueueCheckIntervalSeconds": 5
}Adjust weights to change the balance between priority and fairness.
"Video": {
"StoragePath": "./videos",
"MaxConcurrentDownloads": 2,
"YtDlpPath": "yt-dlp.exe",
"PreferredFormat": "bestvideo+bestaudio/best",
"AdditionalArguments": "--js-runtimes node"
}Note: The format bestvideo+bestaudio downloads the highest quality video and audio streams separately, then merges them using ffmpeg. This enables 1080p, 1440p, and 4K downloads. The --js-runtimes node flag allows yt-dlp to use Node.js for better YouTube extraction.
- Framework: ASP.NET Core 9.0
- UI: Blazor Server (real-time via SignalR)
- Database: Entity Framework Core 9.0 with SQLite
- Authentication: ASP.NET Identity
- Video Playback: LibVLCSharp 3.9.5
- Video Download: yt-dlp (via Process)
- Logging: Serilog (console + file)
SingleStepViewer/
├── Program.cs # Application entry point
├── appsettings.json # Configuration
├── Data/
│ ├── ApplicationDbContext.cs # EF Core DbContext
│ └── Entities/ # Database models
│ ├── ApplicationUser.cs
│ ├── Playlist.cs
│ ├── PlaylistItem.cs
│ ├── PlaybackHistory.cs
│ └── QueueState.cs
├── Services/
│ ├── Interfaces/ # Service contracts
│ ├── UserService.cs
│ ├── PlaylistService.cs
│ ├── PlaylistItemService.cs
│ ├── VideoService.cs # Metadata extraction
│ ├── DownloadService.cs # Video downloading
│ ├── PlaybackService.cs # VLC control
│ ├── QueueManager.cs # Scheduling algorithm
│ └── ConfigurationService.cs
├── BackgroundServices/
│ ├── PlaybackEngineService.cs # Main playback loop
│ └── VideoDownloaderService.cs# Background downloader
├── Components/
│ ├── Pages/ # Blazor pages
│ │ ├── Index.razor # Dashboard
│ │ ├── MyPlaylists.razor # Playlist management
│ │ ├── PlaylistDetail.razor # Add/edit videos
│ │ ├── Queue.razor # Queue visualization
│ │ └── Account/ # Login/Register
│ ├── Shared/ # Shared components
│ │ ├── MainLayout.razor
│ │ └── NavMenu.razor
│ └── Hubs/
│ └── PlaybackHub.cs # SignalR hub
├── Configuration/ # Configuration option classes
└── Migrations/ # EF Core migrations
- Register an account at
/account/register - Login with your credentials
- Create a Playlist from "My Playlists" page
- Add Videos by entering YouTube URLs
- Set Priorities (1-10) for each video
- Videos are automatically:
- Downloaded in the background
- Added to the queue
- Played when their turn comes
- Access admin panel at
/admin(requires Admin role) - View system statistics
- Manage users
- Adjust scheduling weights (future feature)
- User adds video URL → Status:
Pending - Background downloader picks it up → Status:
Downloading - Download completes → Status:
Ready - Queue manager selects it → Status:
Playing - Playback completes → Status:
Played
Errors at any stage set status to Error with details.
Every 5 seconds, the Playback Engine:
- Checks if a video is currently playing
- If not, asks Queue Manager for next video
- Queue Manager:
- Gets all
Readyvideos - Calculates score for each (priority + fairness)
- Returns highest-scoring video
- Gets all
- Playback Engine starts playback
- Updates queue state for fairness tracking
- Broadcasts status via SignalR
SignalR broadcasts these events:
- VideoStarted: When playback begins
- VideoEnded: When playback completes
- VideoError: When playback fails
- QueueUpdated: When queue changes
- VideoDownloadStarted/Completed/Failed: Download status
All connected clients receive updates and refresh automatically.
- AspNetUsers: User accounts (Identity)
- AspNetRoles: User roles (Admin, Regular)
- Playlists: User-owned playlists
- PlaylistItems: Videos in playlists
- PlaybackHistory: Record of played videos
- QueueState: Current playback state + fairness tracking
- User → Playlists (one-to-many)
- Playlist → PlaylistItems (one-to-many)
- PlaylistItem → PlaybackHistory (one-to-many)
- QueueState → CurrentPlaylistItem (one-to-one)
Logs are written to:
- Console: Real-time logging during development
- Files:
logs/log-YYYY-MM-DD.txt(rolls daily)
Log levels:
- Debug: Download progress, queue calculations
- Information: Video started/ended, user actions
- Warning: Download failures (non-critical)
- Error: Critical failures, exceptions
- Ensure
yt-dlp.exeis in PATH or updateYtDlpPathinappsettings.json - Test:
yt-dlp --versionin terminal
- Verify VLC is installed at configured path
- For development without video output: Set
EnableVideoOutput: false - Check logs for detailed error messages
- Check
VideoDownloaderServiceis running (logs show "Video Downloader Service starting") - Verify video URL is supported by yt-dlp
- Check storage path exists and is writable
- View error message in video status
- Ensure ffmpeg is installed:
ffmpeg -version - Ensure Node.js is installed:
node --version - Without ffmpeg, only pre-merged formats are available (typically 720p max)
- Without Node.js, yt-dlp may fail to extract highest quality streams from YouTube
- Ensure SignalR connection is established (check browser console)
- Verify firewall allows WebSocket connections
- Check logs for SignalR errors
- Concurrent Downloads: Limited to 2 by default (configurable)
- Queue Calculation: O(n) where n = ready videos (efficient for 100s of videos)
- Database: SQLite works well for small-medium deployments
- For large deployments, consider PostgreSQL
- Video Storage: Videos kept permanently (manual cleanup required)
- Authentication: ASP.NET Identity with secure password hashing
- Authorization: Role-based (Admin vs Regular users)
- File Upload: Not supported (only URLs) to prevent malicious files
- SQL Injection: Protected by EF Core parameterized queries
- XSS: Protected by Blazor's automatic HTML encoding
Potential improvements:
- Admin UI for adjusting scheduling weights
- Video retention policy (auto-delete old videos)
- Playlist import/export
- Video search and filtering
- User statistics and history
- Multiple video sources (Vimeo, direct URLs)
- Thumbnail generation for non-YouTube videos
- Playlist sharing between users
- Skip/pause controls from web UI
- Video preview before adding
# Run with hot reload
dotnet watch run
# View logs in console
# Set EnableVideoOutput: false to avoid VLC window
# Access Swagger API docs (not fully implemented)
# https://localhost:5001/swagger# Add migration
dotnet ef migrations add MigrationName
# Apply migrations
dotnet ef database update
# Remove last migration
dotnet ef migrations remove- Test user accounts can be created via
/account/register - Use development mode (
EnableVideoOutput: false) to test without VLC - Monitor logs in
logs/directory for debugging
This project is for educational/personal use.
Built with:
- ASP.NET Core 9.0
- Blazor Server
- Entity Framework Core
- LibVLCSharp
- yt-dlp
- Serilog
Note: This system is designed for trusted users in a controlled environment. Videos are automatically downloaded and played - ensure compliance with copyright laws and your organization's policies.