8f860a8ec3
- All DLLs now output to lib\[Configuration]\[TargetFramework]\ at repo root (see Directory.Build.props) - .gitignore updated to exclude /lib/ - On first run, startup.json is auto-generated with OS-appropriate default paths (Windows, Linux, macOS, or portable) - Removes null/example config; generated config is immediately usable and clearly documented - Extensive new documentation: build output, startup.json logic, visual guides, and code proofs - Publish profile now deletes existing files for clean deploys - No breaking changes: existing startup.json files are preserved - Improves first-run UX, deployment, and cross-platform consistency
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
|