Web application for centralized management of Raspberry Pi running LibreELEC/Kodi.
Monitor player status, control playback, transfer video files from a private storage server to Pi devices, and schedule automated tasks.
The application was originally designed for video file management (/storage/videos folder on the Raspberry Pi). As a result, other file formats have not been tested.
Pour le français, voir README.fr.md
- Prerequisites
- Architecture
- Quick Install (Docker)
- Configuration
- Enabling JSON-RPC on Kodi
- Private Storage Server
- Microsoft Authentication
- Application Roles
- Features
- Local Development
- Database Migrations
- Security
- License
| Component | Minimum version |
|---|---|
| Raspberry Pi | 4B and upper |
| LibreELEC | 10.0 |
| Kodi | 19 (Matrix) |
| MySQL | 8.0 |
| Docker + Docker Compose | 24.0 |
| Microsoft account | Azure AD / Entra ID (organizational) |
The system requirements simply reflect the environment for which the application was developed; it may well be possible to use an earlier version of the Raspberry Pi, etc.
┌─────────────────────────────────────────┐
│ Internet │
│ │ HTTPS (OIDC Microsoft) │
│ ┌─────▼──────────┐ │
│ │ KodiNet Web │ Blazor Server │
│ │ (container) │ .NET 8 │
│ └──┬──────────┬──┘ │
│ Internal│network │ │
│ ┌───▼───┐ ┌───▼──────────┐ │
│ │ MySQL │ │ Private │ │
│ │ 8.4 │ │ Storage │ │
│ └───────┘ │ (FastAPI) │ │
│ └──────────────┘ │
│ │
│ Local Pi network │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Pi 1 │ │ Pi 2 │ │ Pi N │ │
│ │ Kodi │ │ Kodi │ │ Kodi │ │
│ └────────┘ └────────┘ └────────┘ │
└─────────────────────────────────────────┘
Application layers:
KodiNet.Domain Entities, enumerations, pure interfaces
KodiNet.Application DTOs, service interfaces, business logic
KodiNet.Infrastructure EF Core/MySQL, Kodi/SFTP/HTTP clients, encryption
KodiNet.Web Blazor Server, MudBlazor components, pages
git clone https://github.com/your-account/kodinet.git
cd kodinetcp .env.example .envEdit .env with your values:
# Azure AD
AZUREAD_TENANTID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZUREAD_CLIENTID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZUREAD_CLIENTSECRET=your_azure_secret
# Database
MYSQL_ROOT_PASSWORD=strong_root_password
MYSQL_DATABASE=kodinet
MYSQL_USER=kodinet_user
MYSQL_PASSWORD=strong_db_password
# Private storage server
STORAGE_API_KEY=long_random_storage_api_key
STORAGE_MAX_SIZE_MB=4096docker compose up -d --buildThe application is available at http://localhost:8080.
Database migrations are applied automatically on startup.
Sign in with a Microsoft account from your organization. The first user to connect automatically receives the Owner role.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "",
"ClientId": "",
"ClientSecret": "",
"CallbackPath": "/signin-oidc"
},
"Storage": {
"BaseUrl": "http://storage:8090",
"ApiKey": "",
"RootPath": "/data"
},
"ConnectionStrings": {
"DefaultConnection": ""
}
}All sensitive values are injected via Docker environment variables (format Section__Key).
Mail configuration is managed from Settings → SMTP Email, accessible only to the Owner role. The SMTP password is encrypted in the database via AES (DataProtection API).
On each Raspberry Pi, from the Kodi interface:
- Settings → Services → Control
- Enable Allow remote control via HTTP
- Optional: set a username and password
- Default port is
8080
The storage server is included in the Docker stack. It exposes a REST API on port 8090 (accessible only from the web container, not from the outside).
| Method | URL | Description |
|---|---|---|
GET |
/api/files?path=/folder |
List folder contents |
GET |
/api/files/download?path=/file |
Download a file |
POST |
/api/files/upload?path=/folder |
Upload a file (multipart) |
POST |
/api/files/folder?path=/folder |
Create a folder |
DELETE |
/api/files?path=/element |
Delete a file or folder |
PATCH |
/api/files/rename |
Rename |
PATCH |
/api/files/move |
Move |
Authentication: X-Api-Key header with the value of STORAGE_API_KEY.
Configurable via STORAGE_MAX_SIZE_MB in .env. Default: 4096 MB (4 GB).
Files are stored in the Docker volume storage_data (persistent). To mount an existing host directory:
# In docker-compose.yml
volumes:
- /path/on/host/videos:/data- Azure Portal → Azure Active Directory → App registrations → New registration
- Name: KodiNet
- Supported account types: Accounts in this organizational directory only
- Redirect URI:
https://your-domain.com/signin-oidc(Web) - After creation, note Application (client) ID →
AZUREAD_CLIENTID - Directory → Directory (tenant) ID →
AZUREAD_TENANTID - Certificates & secrets → New client secret → note the value →
AZUREAD_CLIENTSECRET
No Graph permissions required — KodiNet only uses basic OIDC authentication (OpenID + profile + email).
Roles are managed from Settings → Users. They are stored in the internal database, independently of Azure AD.
| Role | Attribute | Rights |
|---|---|---|
| Owner | Unique — non-revocable — not assignable from the UI | All Admin rights + SMTP config + theme management + import/export + ownership transfer |
| Admin | Managed by Owner | Pi management, establishments, users, scheduler, notifications |
| Operator | Managed by Admin | Kodi player control, file transfers |
| Viewer | Managed by Admin | Read-only — Pi status monitoring |
The first user to sign in to the application automatically receives the Owner role. This mechanism only triggers once (when the user_roles table is empty).
From Settings → Users, the "Transfer Owner Role" button allows the Owner to delegate their role to another user. The current Owner is immediately signed out after the transfer.
- Pi grid with real-time status (Playing / Idle / Offline / Incompatible)
- Filter by establishment, status, text search
- Sort by name, status, establishment (ascending/descending)
- Bulk Pi import from CSV file
- Global stats in the header bar (total / playing / offline)
CSV import format for Pi:
name,ip_address,location,model,kodi_user,kodi_password,kodi_port,ssh_user,ssh_password,ssh_port,video_folder
Hall Pi A,192.168.1.10,North Building,Pi 4B,kodi,password,8080,pi,password,22,/storage/videos
Info & Status tab
- Kodi status, version, CPU temperature, free memory, disk space
- Volume control
- Configuration details
Player & Files tab
- Play/pause, previous/next track, stop
- Repeat mode (Off / One / All)
- File browsing via SFTP
- File upload from browser to Pi
- File renaming
- Direct link to Kodi web interface
- Browse the storage server file tree
- Upload video files from the browser
- Create folders, rename, move, delete
- Send selected files to one or more Pi (background transfer)
- Transfer queue with progress tracking
- Storage operations log (import, deletion, rename, move)
Users (Admin) — Role management and cron notification activation per user
Establishments (Admin) — CRUD for establishments + CSV import/export
Scheduler (Admin)
KodiRestarterjob: restarts idle players (Idle/Stopped) and plays the video folder on repeatKodiRebooterjob: reboots the LibreELEC system on all Pi- Cron expression configuration per job
- Enable/disable per job
- Execution history with per-Pi detail
Themes (Owner) — Create, edit, delete custom themes
SMTP Email (Owner) — Mail server configuration (AES encryption for password)
4 built-in themes: Ruby (bordeaux), Ocean (petrol blue), Amber (warm wood). Each theme has light and dark modes. The mode (System / Light / Dark) is remembered per device in localStorage.
The interface is available in French and English. Language is selected from the user menu (avatar icon in the top right). The choice is persisted per user account and synchronized across all connected devices.
- .NET 8 SDK
- MySQL 8.0 (local or Docker)
- Python 3.12+ (for the storage server)
# Clone
git clone https://github.com/mathiasbenner/kodinet.git
cd kodinet
# Restore packages
dotnet restore KodiNet.sln
# Configure local secrets (never commit these)
cd KodiNet.Web
dotnet user-secrets set "AzureAd:TenantId" "your-tenant-id"
dotnet user-secrets set "AzureAd:ClientId" "your-client-id"
dotnet user-secrets set "AzureAd:ClientSecret" "your-secret"
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;..."
dotnet user-secrets set "Storage:ApiKey" "your-key"
# Start the storage server (optional)
cd ../KodiNet.API
pip install -r requirements.txt
python main.py
# Start the application
cd ../KodiNet.Web
dotnet runservices:
db:
ports:
- "3306:3306" # direct access from the IDE
web:
environment:
ASPNETCORE_ENVIRONMENT: DevelopmentMigrations are applied automatically on application startup.
To create a new migration after modifying an entity:
dotnet ef migrations add MigrationName \
--project KodiNet.Infrastructure \
--startup-project KodiNet.Web- Authentication: Microsoft OIDC — only accounts from your Azure AD organization can sign in
- Authorization: application roles stored in database, loaded via
IClaimsTransformation - Kodi/SSH credentials: encrypted in database via AES (DataProtection API)
- SMTP password: encrypted in database via AES
- Storage API Key: injected via environment variable, never exposed client-side
- Docker network: MySQL and storage server on
internal: truenetwork — inaccessible from outside - No secrets in the repository:
.envandappsettings.*.jsonare gitignored
MIT — see LICENSE.
Main dependencies and their licenses:
| Package | License |
|---|---|
| MudBlazor | MIT |
| Microsoft.Identity.Web | MIT |
| Entity Framework Core | MIT |
| Pomelo.EntityFrameworkCore.MySql | MIT |
| SSH.NET | MIT |
| Cronos | MIT |