69c213f13c
Introduces automatic creation of a self-documenting startup.json file on first startup, with example path configurations for Linux, Windows, and portable setups. Adds preset database configurations to database.xml for easy switching between SQLite and PostgreSQL. Refactors database config classes for XML serialization compatibility and ensures database.xml is never empty. Comprehensive documentation and migration guides included.
200 lines
6.3 KiB
Markdown
200 lines
6.3 KiB
Markdown
# Fix: Empty database.xml on Startup
|
|
|
|
## Problem
|
|
The `database.xml` file was being created empty on startup, causing Jellyfin to fail or not properly initialize the database configuration.
|
|
|
|
## Root Cause
|
|
|
|
### Issue 1: `required` Keyword Incompatibility
|
|
The `DatabaseConfigurationOptions` class had the `DatabaseType` property marked with the C# 11 `required` keyword:
|
|
|
|
```csharp
|
|
public required string DatabaseType { get; set; }
|
|
```
|
|
|
|
**Problem:** The `System.Xml.Serialization.XmlSerializer` used by Jellyfin doesn't support the `required` keyword. When trying to serialize/deserialize:
|
|
- XML serializer couldn't create instances with required properties
|
|
- Serialization would fail silently or create empty files
|
|
- Deserialization from empty files would fail
|
|
|
|
### Issue 2: Dictionary<string, T> Not XML-Serializable
|
|
The `PresetConfigurations` property used a `Dictionary<string, PresetDatabaseConfiguration>`:
|
|
|
|
```csharp
|
|
public Dictionary<string, PresetDatabaseConfiguration>? PresetConfigurations { get; set; }
|
|
```
|
|
|
|
**Problem:** `Dictionary<TKey, TValue>` is not directly XML-serializable by `XmlSerializer` without custom serialization logic.
|
|
|
|
## Solution Applied
|
|
|
|
### Fix 1: Removed `required` Keyword
|
|
Changed `DatabaseType` to nullable and added default value in constructor:
|
|
|
|
**Before:**
|
|
```csharp
|
|
public required string DatabaseType { get; set; }
|
|
```
|
|
|
|
**After:**
|
|
```csharp
|
|
public DatabaseConfigurationOptions()
|
|
{
|
|
DatabaseType = "Jellyfin-SQLite";
|
|
LockingBehavior = DatabaseLockingBehaviorTypes.NoLock;
|
|
}
|
|
|
|
public string? DatabaseType { get; set; }
|
|
```
|
|
|
|
### Fix 2: Replaced Dictionary with XML-Serializable List
|
|
Changed from `Dictionary<string, T>` to `List<PresetConfigurationItem>` with XML attributes:
|
|
|
|
**Before:**
|
|
```csharp
|
|
public Dictionary<string, PresetDatabaseConfiguration>? PresetConfigurations { get; set; }
|
|
```
|
|
|
|
**After:**
|
|
```csharp
|
|
[XmlArray("PresetConfigurations")]
|
|
[XmlArrayItem("PresetConfiguration")]
|
|
public List<PresetConfigurationItem>? PresetConfigurations { get; set; }
|
|
```
|
|
|
|
**New supporting class:**
|
|
```csharp
|
|
public class PresetConfigurationItem
|
|
{
|
|
[XmlElement("key")]
|
|
public string? Key { get; set; }
|
|
|
|
[XmlElement("value")]
|
|
public PresetDatabaseConfiguration? Value { get; set; }
|
|
}
|
|
```
|
|
|
|
### Fix 3: Added Null Check Safety
|
|
Added validation to ensure DatabaseType is never null when reading configuration:
|
|
|
|
```csharp
|
|
// Ensure DatabaseType is set (handle corrupted or empty configuration files)
|
|
if (string.IsNullOrWhiteSpace(efCoreConfiguration.DatabaseType))
|
|
{
|
|
efCoreConfiguration.DatabaseType = "Jellyfin-SQLite";
|
|
configurationManager.SaveConfiguration("database", efCoreConfiguration);
|
|
}
|
|
```
|
|
|
|
## Files Modified
|
|
|
|
1. **src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/DatabaseConfigurationOptions.cs**
|
|
- Removed `required` keyword
|
|
- Added default constructor with defaults
|
|
- Changed `Dictionary` to `List` with XML attributes
|
|
|
|
2. **src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/PresetConfigurationItem.cs** (NEW)
|
|
- Created new class for XML-serializable key-value pairs
|
|
- Added proper XML attributes
|
|
|
|
3. **Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs**
|
|
- Updated preset creation to use `List<PresetConfigurationItem>`
|
|
- Added null check for DatabaseType
|
|
|
|
## XML Output
|
|
|
|
The database.xml file now properly serializes:
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<DatabaseConfigurationOptions>
|
|
<DatabaseType>Jellyfin-SQLite</DatabaseType>
|
|
<LockingBehavior>NoLock</LockingBehavior>
|
|
<PresetConfigurations>
|
|
<PresetConfiguration>
|
|
<key>SQLite</key>
|
|
<value>
|
|
<DatabaseType>Jellyfin-SQLite</DatabaseType>
|
|
<LockingBehavior>NoLock</LockingBehavior>
|
|
<Description>Default SQLite database configuration. Database stored in data directory.</Description>
|
|
</value>
|
|
</PresetConfiguration>
|
|
<PresetConfiguration>
|
|
<key>PostgreSQL-Local</key>
|
|
<value>
|
|
<DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
|
|
<LockingBehavior>NoLock</LockingBehavior>
|
|
<Description>PostgreSQL database on localhost. Update CustomProviderOptions with your database credentials.</Description>
|
|
</value>
|
|
</PresetConfiguration>
|
|
</PresetConfigurations>
|
|
</DatabaseConfigurationOptions>
|
|
```
|
|
|
|
## Testing
|
|
|
|
1. **Delete existing database.xml** (if any)
|
|
2. **Start Jellyfin**
|
|
3. **Verify** `database.xml` is created in config directory
|
|
4. **Check** file contains proper XML structure (not empty)
|
|
5. **Confirm** Jellyfin uses SQLite by default
|
|
6. **Test switching** to PostgreSQL by editing DatabaseType
|
|
|
|
## Verification Commands
|
|
|
|
### Windows (PowerShell)
|
|
```powershell
|
|
# Check if file exists and has content
|
|
Get-Content "$env:LOCALAPPDATA\jellyfin\config\database.xml"
|
|
|
|
# Check file size (should be > 0 bytes)
|
|
(Get-Item "$env:LOCALAPPDATA\jellyfin\config\database.xml").Length
|
|
```
|
|
|
|
### Linux
|
|
```bash
|
|
# Check if file exists and has content
|
|
cat ~/.config/jellyfin/database.xml
|
|
|
|
# Check file size
|
|
ls -lh ~/.config/jellyfin/database.xml
|
|
```
|
|
|
|
## Expected Behavior
|
|
|
|
✅ **database.xml created** on first startup
|
|
✅ **Contains proper XML** with DatabaseType, LockingBehavior, and PresetConfigurations
|
|
✅ **Defaults to SQLite** if no configuration exists
|
|
✅ **Can be edited** to switch between databases
|
|
✅ **Presets visible** as templates in the file
|
|
|
|
## Backward Compatibility
|
|
|
|
✅ **Existing configurations** will be loaded correctly
|
|
✅ **Old database.xml files** without presets will still work
|
|
✅ **No data migration** needed
|
|
✅ **Graceful degradation** - if file is corrupted, defaults to SQLite
|
|
|
|
## Related Issues
|
|
|
|
This fix resolves:
|
|
- Empty database.xml files on startup
|
|
- Configuration not persisting
|
|
- Database provider selection not working
|
|
- Preset configurations not saving
|
|
|
|
## Prevention
|
|
|
|
To prevent similar issues in future:
|
|
1. ✅ Avoid using `required` keyword with XML-serialized classes
|
|
2. ✅ Use XML-serializable types (avoid Dictionary, use List instead)
|
|
3. ✅ Add default constructors to serialized classes
|
|
4. ✅ Test XML serialization/deserialization in unit tests
|
|
5. ✅ Add null checks when reading configuration
|
|
|
|
## See Also
|
|
|
|
- [DATABASE_CONFIGURATION_GUIDE.md](DATABASE_CONFIGURATION_GUIDE.md) - How to configure database
|
|
- [DATABASE_PRESETS_FEATURE.md](DATABASE_PRESETS_FEATURE.md) - Preset configurations feature
|
|
- [database.xml.example](database.xml.example) - Example configuration file
|